From 489468feaa08b8f504735eecca522fb8d0f825d2 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Wed, 11 Jun 2008 16:46:03 +0000 Subject: [PATCH] renaming git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@54119 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/osx/carbon/Info.plist.in | 36 + src/osx/carbon/aboutdlg.cpp | 92 + src/osx/carbon/accel.cpp | 109 + src/osx/carbon/app.cpp | 1648 ++++++++ src/osx/carbon/apprsrc.h | 1 + src/osx/carbon/apprsrc.r | 1 + src/osx/carbon/artmac.cpp | 108 + src/osx/carbon/bitmap.cpp | 1721 ++++++++ src/osx/carbon/bmpbuttn.cpp | 189 + src/osx/carbon/brush.cpp | 192 + src/osx/carbon/button.cpp | 273 ++ src/osx/carbon/carbrsrc.r | 7 + src/osx/carbon/cfsocket.cpp | 2311 +++++++++++ src/osx/carbon/checkbox.cpp | 186 + src/osx/carbon/checklst.cpp | 273 ++ src/osx/carbon/choice.cpp | 308 ++ src/osx/carbon/clipbrd.cpp | 209 + src/osx/carbon/colordlg.cpp | 75 + src/osx/carbon/colordlgosx.mm | 193 + src/osx/carbon/colour.cpp | 147 + src/osx/carbon/combobox.cpp | 668 +++ src/osx/carbon/combobxc.cpp | 793 ++++ src/osx/carbon/control.cpp | 97 + src/osx/carbon/corersrc.r | 159 + src/osx/carbon/cursor.cpp | 697 ++++ src/osx/carbon/databrow.cpp | 1145 ++++++ src/osx/carbon/dataobj.cpp | 753 ++++ src/osx/carbon/dataview.cpp | 1575 +++++++ src/osx/carbon/dcclient.cpp | 194 + src/osx/carbon/dcmemory.cpp | 116 + src/osx/carbon/dcprint.cpp | 393 ++ src/osx/carbon/dcscreen.cpp | 59 + src/osx/carbon/dialog.cpp | 185 + src/osx/carbon/dirdlg.cpp | 145 + src/osx/carbon/dirmac.cpp | 269 ++ src/osx/carbon/display.cpp | 255 ++ src/osx/carbon/dnd.cpp | 562 +++ src/osx/carbon/drawer.cpp | 199 + src/osx/carbon/dummy.txt | 0 src/osx/carbon/evtloop.cpp | 109 + src/osx/carbon/filedlg.cpp | 436 ++ src/osx/carbon/font.cpp | 1021 +++++ src/osx/carbon/fontdlg.cpp | 828 ++++ src/osx/carbon/fontdlgosx.mm | 535 +++ src/osx/carbon/fontenum.cpp | 123 + src/osx/carbon/fontutil.cpp | 88 + src/osx/carbon/frame.cpp | 402 ++ src/osx/carbon/gauge.cpp | 128 + src/osx/carbon/gdiobj.cpp | 75 + src/osx/carbon/glcanvas.cpp | 499 +++ src/osx/carbon/graphics.cpp | 2594 ++++++++++++ src/osx/carbon/helpxxxx.cpp | 80 + src/osx/carbon/icon.cpp | 317 ++ src/osx/carbon/imaglist.cpp | 309 ++ src/osx/carbon/joystick.cpp | 1 + src/osx/carbon/listbox.cpp | 628 +++ src/osx/carbon/listctrl_mac.cpp | 3382 +++++++++++++++ src/osx/carbon/main.cpp | 12 + src/osx/carbon/mdi.cpp | 514 +++ src/osx/carbon/mediactrl.cpp | 1243 ++++++ src/osx/carbon/menu.cpp | 1183 ++++++ src/osx/carbon/menuitem.cpp | 289 ++ src/osx/carbon/metafile.cpp | 366 ++ src/osx/carbon/mimetmac.cpp | 1953 +++++++++ src/osx/carbon/minifram.cpp | 20 + src/osx/carbon/morefile/DirectoryCopy.c | 685 ++++ src/osx/carbon/morefile/DirectoryCopy.h | 559 +++ src/osx/carbon/morefile/FSpCompat.c | 946 +++++ src/osx/carbon/morefile/FSpCompat.h | 586 +++ src/osx/carbon/morefile/FileCopy.c | 611 +++ src/osx/carbon/morefile/FileCopy.h | 271 ++ src/osx/carbon/morefile/FullPath.c | 282 ++ src/osx/carbon/morefile/FullPath.h | 311 ++ src/osx/carbon/morefile/IterateDirectory.c | 207 + src/osx/carbon/morefile/IterateDirectory.h | 222 + src/osx/carbon/morefile/MoreDesktopMgr.c | 1270 ++++++ src/osx/carbon/morefile/MoreDesktopMgr.h | 628 +++ src/osx/carbon/morefile/MoreFiles.c | 643 +++ src/osx/carbon/morefile/MoreFiles.h | 1420 +++++++ src/osx/carbon/morefile/MoreFilesExtras.c | 3632 +++++++++++++++++ src/osx/carbon/morefile/MoreFilesExtras.h | 3597 ++++++++++++++++ src/osx/carbon/morefile/Optimization.h | 109 + src/osx/carbon/morefile/OptimizationEnd.h | 56 + src/osx/carbon/morefile/Search.c | 1300 ++++++ src/osx/carbon/morefile/Search.h | 304 ++ src/osx/carbon/morefilex/MoreFilesX.c | 2782 +++++++++++++ src/osx/carbon/morefilex/MoreFilesX.cpp | 2772 +++++++++++++ src/osx/carbon/morefilex/MoreFilesX.h | 1825 +++++++++ src/osx/carbon/morefilex/MoreFilesXReadMe.txt | 125 + src/osx/carbon/msgdlg.cpp | 284 ++ src/osx/carbon/nonownedwnd.cpp | 1689 ++++++++ src/osx/carbon/notebmac.cpp | 621 +++ src/osx/carbon/overlay.cpp | 185 + src/osx/carbon/palette.cpp | 155 + src/osx/carbon/pen.cpp | 308 ++ src/osx/carbon/popupwin.cpp | 59 + src/osx/carbon/printdlg.cpp | 292 ++ src/osx/carbon/printmac.cpp | 620 +++ src/osx/carbon/radiobox.cpp | 527 +++ src/osx/carbon/radiobut.cpp | 178 + src/osx/carbon/region.cpp | 550 +++ src/osx/carbon/renderer.cpp | 408 ++ src/osx/carbon/scrolbar.cpp | 247 ++ src/osx/carbon/settings.cpp | 256 ++ src/osx/carbon/slider.cpp | 557 +++ src/osx/carbon/sound.cpp | 476 +++ src/osx/carbon/spinbutt.cpp | 208 + src/osx/carbon/spinctrl.cpp | 434 ++ src/osx/carbon/srchctrl.cpp | 398 ++ src/osx/carbon/statbmp.cpp | 86 + src/osx/carbon/statbox.cpp | 76 + src/osx/carbon/statbrma.cpp | 181 + src/osx/carbon/statline.cpp | 44 + src/osx/carbon/statlmac.cpp | 68 + src/osx/carbon/stattext.cpp | 197 + src/osx/carbon/tabctrl.cpp | 202 + src/osx/carbon/taskbar.cpp | 507 +++ src/osx/carbon/textctrl.cpp | 3129 ++++++++++++++ src/osx/carbon/tglbtn.cpp | 174 + src/osx/carbon/thread.cpp | 1774 ++++++++ src/osx/carbon/timer.cpp | 99 + src/osx/carbon/toolbar.cpp | 1735 ++++++++ src/osx/carbon/tooltip.cpp | 263 ++ src/osx/carbon/toplevel.cpp | 274 ++ src/osx/carbon/treectrl.cpp | 418 ++ src/osx/carbon/uma.cpp | 316 ++ src/osx/carbon/utils.cpp | 2010 +++++++++ src/osx/carbon/utilscocoa.mm | 304 ++ src/osx/carbon/window.cpp | 3277 +++++++++++++++ src/osx/carbon/wxmac.icns | Bin 0 -> 36901 bytes src/osx/core/cfstring.cpp | 732 ++++ src/osx/core/gsockosx.cpp | 318 ++ src/osx/core/hid.cpp | 741 ++++ src/osx/core/hidjoystick.cpp | 905 ++++ src/osx/core/stdpaths_cf.cpp | 217 + src/osx/core/strconv_cf.cpp | 228 ++ src/osx/core/utilsexc_base.cpp | 229 ++ src/osx/core/utilsexc_cf.cpp | 118 + src/osx/wxmac.icns | Bin 0 -> 45458 bytes 139 files changed, 85695 insertions(+) create mode 100644 src/osx/carbon/Info.plist.in create mode 100644 src/osx/carbon/aboutdlg.cpp create mode 100644 src/osx/carbon/accel.cpp create mode 100644 src/osx/carbon/app.cpp create mode 100644 src/osx/carbon/apprsrc.h create mode 100644 src/osx/carbon/apprsrc.r create mode 100644 src/osx/carbon/artmac.cpp create mode 100644 src/osx/carbon/bitmap.cpp create mode 100644 src/osx/carbon/bmpbuttn.cpp create mode 100644 src/osx/carbon/brush.cpp create mode 100644 src/osx/carbon/button.cpp create mode 100644 src/osx/carbon/carbrsrc.r create mode 100644 src/osx/carbon/cfsocket.cpp create mode 100644 src/osx/carbon/checkbox.cpp create mode 100644 src/osx/carbon/checklst.cpp create mode 100644 src/osx/carbon/choice.cpp create mode 100644 src/osx/carbon/clipbrd.cpp create mode 100644 src/osx/carbon/colordlg.cpp create mode 100644 src/osx/carbon/colordlgosx.mm create mode 100644 src/osx/carbon/colour.cpp create mode 100644 src/osx/carbon/combobox.cpp create mode 100644 src/osx/carbon/combobxc.cpp create mode 100644 src/osx/carbon/control.cpp create mode 100644 src/osx/carbon/corersrc.r create mode 100644 src/osx/carbon/cursor.cpp create mode 100644 src/osx/carbon/databrow.cpp create mode 100644 src/osx/carbon/dataobj.cpp create mode 100644 src/osx/carbon/dataview.cpp create mode 100644 src/osx/carbon/dcclient.cpp create mode 100644 src/osx/carbon/dcmemory.cpp create mode 100644 src/osx/carbon/dcprint.cpp create mode 100644 src/osx/carbon/dcscreen.cpp create mode 100644 src/osx/carbon/dialog.cpp create mode 100644 src/osx/carbon/dirdlg.cpp create mode 100644 src/osx/carbon/dirmac.cpp create mode 100644 src/osx/carbon/display.cpp create mode 100644 src/osx/carbon/dnd.cpp create mode 100644 src/osx/carbon/drawer.cpp create mode 100644 src/osx/carbon/dummy.txt create mode 100644 src/osx/carbon/evtloop.cpp create mode 100644 src/osx/carbon/filedlg.cpp create mode 100644 src/osx/carbon/font.cpp create mode 100644 src/osx/carbon/fontdlg.cpp create mode 100644 src/osx/carbon/fontdlgosx.mm create mode 100644 src/osx/carbon/fontenum.cpp create mode 100644 src/osx/carbon/fontutil.cpp create mode 100644 src/osx/carbon/frame.cpp create mode 100644 src/osx/carbon/gauge.cpp create mode 100644 src/osx/carbon/gdiobj.cpp create mode 100644 src/osx/carbon/glcanvas.cpp create mode 100644 src/osx/carbon/graphics.cpp create mode 100644 src/osx/carbon/helpxxxx.cpp create mode 100644 src/osx/carbon/icon.cpp create mode 100644 src/osx/carbon/imaglist.cpp create mode 100644 src/osx/carbon/joystick.cpp create mode 100644 src/osx/carbon/listbox.cpp create mode 100644 src/osx/carbon/listctrl_mac.cpp create mode 100644 src/osx/carbon/main.cpp create mode 100644 src/osx/carbon/mdi.cpp create mode 100644 src/osx/carbon/mediactrl.cpp create mode 100644 src/osx/carbon/menu.cpp create mode 100644 src/osx/carbon/menuitem.cpp create mode 100644 src/osx/carbon/metafile.cpp create mode 100644 src/osx/carbon/mimetmac.cpp create mode 100644 src/osx/carbon/minifram.cpp create mode 100644 src/osx/carbon/morefile/DirectoryCopy.c create mode 100644 src/osx/carbon/morefile/DirectoryCopy.h create mode 100644 src/osx/carbon/morefile/FSpCompat.c create mode 100644 src/osx/carbon/morefile/FSpCompat.h create mode 100644 src/osx/carbon/morefile/FileCopy.c create mode 100644 src/osx/carbon/morefile/FileCopy.h create mode 100644 src/osx/carbon/morefile/FullPath.c create mode 100644 src/osx/carbon/morefile/FullPath.h create mode 100644 src/osx/carbon/morefile/IterateDirectory.c create mode 100644 src/osx/carbon/morefile/IterateDirectory.h create mode 100644 src/osx/carbon/morefile/MoreDesktopMgr.c create mode 100644 src/osx/carbon/morefile/MoreDesktopMgr.h create mode 100644 src/osx/carbon/morefile/MoreFiles.c create mode 100644 src/osx/carbon/morefile/MoreFiles.h create mode 100644 src/osx/carbon/morefile/MoreFilesExtras.c create mode 100644 src/osx/carbon/morefile/MoreFilesExtras.h create mode 100644 src/osx/carbon/morefile/Optimization.h create mode 100644 src/osx/carbon/morefile/OptimizationEnd.h create mode 100644 src/osx/carbon/morefile/Search.c create mode 100644 src/osx/carbon/morefile/Search.h create mode 100644 src/osx/carbon/morefilex/MoreFilesX.c create mode 100644 src/osx/carbon/morefilex/MoreFilesX.cpp create mode 100644 src/osx/carbon/morefilex/MoreFilesX.h create mode 100644 src/osx/carbon/morefilex/MoreFilesXReadMe.txt create mode 100644 src/osx/carbon/msgdlg.cpp create mode 100644 src/osx/carbon/nonownedwnd.cpp create mode 100644 src/osx/carbon/notebmac.cpp create mode 100644 src/osx/carbon/overlay.cpp create mode 100644 src/osx/carbon/palette.cpp create mode 100644 src/osx/carbon/pen.cpp create mode 100644 src/osx/carbon/popupwin.cpp create mode 100644 src/osx/carbon/printdlg.cpp create mode 100644 src/osx/carbon/printmac.cpp create mode 100644 src/osx/carbon/radiobox.cpp create mode 100644 src/osx/carbon/radiobut.cpp create mode 100644 src/osx/carbon/region.cpp create mode 100644 src/osx/carbon/renderer.cpp create mode 100644 src/osx/carbon/scrolbar.cpp create mode 100644 src/osx/carbon/settings.cpp create mode 100644 src/osx/carbon/slider.cpp create mode 100644 src/osx/carbon/sound.cpp create mode 100644 src/osx/carbon/spinbutt.cpp create mode 100644 src/osx/carbon/spinctrl.cpp create mode 100644 src/osx/carbon/srchctrl.cpp create mode 100644 src/osx/carbon/statbmp.cpp create mode 100644 src/osx/carbon/statbox.cpp create mode 100644 src/osx/carbon/statbrma.cpp create mode 100644 src/osx/carbon/statline.cpp create mode 100644 src/osx/carbon/statlmac.cpp create mode 100644 src/osx/carbon/stattext.cpp create mode 100644 src/osx/carbon/tabctrl.cpp create mode 100644 src/osx/carbon/taskbar.cpp create mode 100644 src/osx/carbon/textctrl.cpp create mode 100644 src/osx/carbon/tglbtn.cpp create mode 100644 src/osx/carbon/thread.cpp create mode 100644 src/osx/carbon/timer.cpp create mode 100644 src/osx/carbon/toolbar.cpp create mode 100644 src/osx/carbon/tooltip.cpp create mode 100644 src/osx/carbon/toplevel.cpp create mode 100644 src/osx/carbon/treectrl.cpp create mode 100644 src/osx/carbon/uma.cpp create mode 100644 src/osx/carbon/utils.cpp create mode 100644 src/osx/carbon/utilscocoa.mm create mode 100644 src/osx/carbon/window.cpp create mode 100644 src/osx/carbon/wxmac.icns create mode 100644 src/osx/core/cfstring.cpp create mode 100644 src/osx/core/gsockosx.cpp create mode 100644 src/osx/core/hid.cpp create mode 100644 src/osx/core/hidjoystick.cpp create mode 100644 src/osx/core/stdpaths_cf.cpp create mode 100644 src/osx/core/strconv_cf.cpp create mode 100644 src/osx/core/utilsexc_base.cpp create mode 100644 src/osx/core/utilsexc_cf.cpp create mode 100644 src/osx/wxmac.icns diff --git a/src/osx/carbon/Info.plist.in b/src/osx/carbon/Info.plist.in new file mode 100644 index 0000000000..de35d6de27 --- /dev/null +++ b/src/osx/carbon/Info.plist.in @@ -0,0 +1,36 @@ + + + + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleIdentifier + org.wxwindows.IDENTIFIER + CFBundleDevelopmentRegion + English + CFBundleExecutable + EXECUTABLE + CFBundleIconFile + wxmac.icns + CFBundleName + EXECUTABLE + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + VERSION + CFBundleShortVersionString + VERSION + CFBundleGetInfoString + EXECUTABLE version VERSION, (c) 2002-2006 wxWidgets + CFBundleLongVersionString + VERSION, (c) 2002-2006 wxWidgets + NSHumanReadableCopyright + Copyright 2002-2006 wxWidgets + LSRequiresCarbon + + CSResourcesFileMapped + + + diff --git a/src/osx/carbon/aboutdlg.cpp b/src/osx/carbon/aboutdlg.cpp new file mode 100644 index 0000000000..243de8b7d7 --- /dev/null +++ b/src/osx/carbon/aboutdlg.cpp @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: mac/carbon/aboutdlg.cpp +// Purpose: native wxAboutBox() implementation for wxMac +// Author: Vadim Zeitlin +// Created: 2006-10-08 +// RCS-ID: $Id$ +// Copyright: (c) 2006 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// for compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_ABOUTDLG + +#ifndef WX_PRECOMP +#endif //WX_PRECOMP + +#include "wx/aboutdlg.h" +#include "wx/generic/aboutdlgg.h" + +#include "wx/mac/private.h" + +// helper class for HIAboutBox options +class AboutBoxOptions : public wxCFRef +{ +public: + AboutBoxOptions() : wxCFRef + ( + CFDictionaryCreateMutable + ( + kCFAllocatorDefault, + 4, // there are at most 4 values + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks + ) + ) + { + } + + void Set(CFStringRef key, const wxString& value) + { + CFDictionarySetValue(*this, key, wxCFStringRef(value)); + } +}; + +// ============================================================================ +// implementation +// ============================================================================ + +void wxAboutBox(const wxAboutDialogInfo& info) +{ + // Mac native about box currently can show only name, version, copyright + // and description fields and we also shoehorn the credits text into the + // description but if we have anything else we must use the generic version +#ifndef __LP64__ + if ( info.IsSimple() ) + { + AboutBoxOptions opts; + + opts.Set(kHIAboutBoxNameKey, info.GetName()); + + if ( info.HasVersion() ) + { + opts.Set(kHIAboutBoxVersionKey, + wxString::Format(_("Version %s"), info.GetVersion())); + } + + if ( info.HasCopyright() ) + opts.Set(kHIAboutBoxCopyrightKey, info.GetCopyright()); + + opts.Set(kHIAboutBoxDescriptionKey, info.GetDescriptionAndCredits()); + + HIAboutBox(opts); + } + else // simple "native" version is not enough +#endif + { + // we need to use the full-blown generic version + wxGenericAboutBox(info); + } +} + +#endif // wxUSE_ABOUTDLG diff --git a/src/osx/carbon/accel.cpp b/src/osx/carbon/accel.cpp new file mode 100644 index 0000000000..f33bbe28e0 --- /dev/null +++ b/src/osx/carbon/accel.cpp @@ -0,0 +1,109 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/accel.cpp +// Purpose: wxAcceleratorTable +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/accel.h" + +#ifndef WX_PRECOMP + #include "wx/string.h" +#endif + +#ifndef __WXUNIVERSAL__ + +IMPLEMENT_DYNAMIC_CLASS(wxAcceleratorTable, wxObject) + +// ---------------------------------------------------------------------------- +// wxAccelList: a list of wxAcceleratorEntries +// ---------------------------------------------------------------------------- + +WX_DECLARE_LIST(wxAcceleratorEntry, wxAccelList); +#include "wx/listimpl.cpp" +WX_DEFINE_LIST(wxAccelList) + +// ---------------------------------------------------------------------------- +// wxAccelRefData: the data used by wxAcceleratorTable +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxAcceleratorRefData: public wxObjectRefData +{ + friend class wxAcceleratorTable; +public: + wxAcceleratorRefData(); + virtual ~wxAcceleratorRefData(); + + wxAccelList m_accels; +}; + +#define M_ACCELDATA ((wxAcceleratorRefData *)m_refData) + +wxAcceleratorRefData::wxAcceleratorRefData() + : m_accels() +{ +} + +wxAcceleratorRefData::~wxAcceleratorRefData() +{ + WX_CLEAR_LIST( wxAccelList, m_accels ); +} + +wxAcceleratorTable::wxAcceleratorTable() +{ + m_refData = NULL; +} + +wxAcceleratorTable::~wxAcceleratorTable() +{ +} + +// Create from an array +wxAcceleratorTable::wxAcceleratorTable(int n, const wxAcceleratorEntry entries[]) +{ + m_refData = new wxAcceleratorRefData; + + for (int i = 0; i < n; i++) + { + int flag = entries[i].GetFlags(); + int keycode = entries[i].GetKeyCode(); + int command = entries[i].GetCommand(); + if ((keycode >= (int)'a') && (keycode <= (int)'z')) keycode = (int)toupper( (char)keycode ); + M_ACCELDATA->m_accels.Append( new wxAcceleratorEntry( flag, keycode, command ) ); + } +} + +bool wxAcceleratorTable::IsOk() const +{ + return (m_refData != NULL); +} + +int wxAcceleratorTable::GetCommand( wxKeyEvent &event ) +{ + if (!Ok()) return -1; + + wxAccelList::compatibility_iterator node = M_ACCELDATA->m_accels.GetFirst(); + while (node) + { + wxAcceleratorEntry *entry = node->GetData(); + if ((event.m_keyCode == entry->GetKeyCode()) && + (((entry->GetFlags() & wxACCEL_CTRL) != 0) == event.ControlDown()) && + (((entry->GetFlags() & wxACCEL_SHIFT) != 0) == event.ShiftDown()) && + (((entry->GetFlags() & wxACCEL_ALT) != 0) == event.AltDown()) && + (((entry->GetFlags() & wxACCEL_CMD) != 0) == event.CmdDown())) + { + return entry->GetCommand(); + } + node = node->GetNext(); + } + + return -1; +} + +#endif \ No newline at end of file diff --git a/src/osx/carbon/app.cpp b/src/osx/carbon/app.cpp new file mode 100644 index 0000000000..a75a511279 --- /dev/null +++ b/src/osx/carbon/app.cpp @@ -0,0 +1,1648 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/app.cpp +// Purpose: wxApp +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/app.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/utils.h" + #include "wx/window.h" + #include "wx/frame.h" + #include "wx/dc.h" + #include "wx/button.h" + #include "wx/menu.h" + #include "wx/pen.h" + #include "wx/brush.h" + #include "wx/palette.h" + #include "wx/icon.h" + #include "wx/cursor.h" + #include "wx/dialog.h" + #include "wx/msgdlg.h" + #include "wx/textctrl.h" + #include "wx/memory.h" + #include "wx/gdicmn.h" + #include "wx/module.h" +#endif + +#include "wx/tooltip.h" +#include "wx/docview.h" +#include "wx/filename.h" +#include "wx/link.h" +#include "wx/thread.h" + +#include + +// mac + +#include "wx/mac/uma.h" + +#ifdef __DARWIN__ +# include +# if defined(WXMAKINGDLL_CORE) +# include +# endif +#endif + +// Keep linker from discarding wxStockGDIMac +wxFORCE_LINK_MODULE(gdiobj) + +// statics for implementation +static bool s_inYield = false; +static bool s_inReceiveEvent = false ; +static EventTime sleepTime = kEventDurationNoWait ; + + +IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler) +BEGIN_EVENT_TABLE(wxApp, wxEvtHandler) + EVT_IDLE(wxApp::OnIdle) + EVT_END_SESSION(wxApp::OnEndSession) + EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession) +END_EVENT_TABLE() + + +// platform specific static variables +static const short kwxMacAppleMenuId = 1 ; + +wxWindow* wxApp::s_captureWindow = NULL ; +long wxApp::s_lastModifiers = 0 ; + +long wxApp::s_macAboutMenuItemId = wxID_ABOUT ; +long wxApp::s_macPreferencesMenuItemId = wxID_PREFERENCES ; +long wxApp::s_macExitMenuItemId = wxID_EXIT ; +wxString wxApp::s_macHelpMenuTitleName = wxT("&Help") ; + +bool wxApp::sm_isEmbedded = false; // Normally we're not a plugin + +//---------------------------------------------------------------------- +// Core Apple Event Support +//---------------------------------------------------------------------- + +pascal OSErr AEHandleODoc( const AppleEvent *event , AppleEvent *reply , SRefCon refcon ) ; +pascal OSErr AEHandleOApp( const AppleEvent *event , AppleEvent *reply , SRefCon refcon ) ; +pascal OSErr AEHandlePDoc( const AppleEvent *event , AppleEvent *reply , SRefCon refcon ) ; +pascal OSErr AEHandleQuit( const AppleEvent *event , AppleEvent *reply , SRefCon refcon ) ; +pascal OSErr AEHandleRApp( const AppleEvent *event , AppleEvent *reply , SRefCon refcon ) ; + +pascal OSErr AEHandleODoc( const AppleEvent *event , AppleEvent *reply , SRefCon WXUNUSED(refcon) ) +{ + return wxTheApp->MacHandleAEODoc( (AppleEvent*) event , reply) ; +} + +pascal OSErr AEHandleOApp( const AppleEvent *event , AppleEvent *reply , SRefCon WXUNUSED(refcon) ) +{ + return wxTheApp->MacHandleAEOApp( (AppleEvent*) event , reply ) ; +} + +pascal OSErr AEHandlePDoc( const AppleEvent *event , AppleEvent *reply , SRefCon WXUNUSED(refcon) ) +{ + return wxTheApp->MacHandleAEPDoc( (AppleEvent*) event , reply ) ; +} + +pascal OSErr AEHandleQuit( const AppleEvent *event , AppleEvent *reply , SRefCon WXUNUSED(refcon) ) +{ + return wxTheApp->MacHandleAEQuit( (AppleEvent*) event , reply) ; +} + +pascal OSErr AEHandleRApp( const AppleEvent *event , AppleEvent *reply , SRefCon WXUNUSED(refcon) ) +{ + return wxTheApp->MacHandleAERApp( (AppleEvent*) event , reply) ; +} + +pascal OSErr AEHandleGURL( const AppleEvent *event , AppleEvent *reply , SRefCon WXUNUSED(refcon) ) +{ + return wxTheApp->MacHandleAEGURL((WXEVENTREF *)event , reply) ; +} + + +// AEODoc Calls MacOpenFile on each of the files passed + +short wxApp::MacHandleAEODoc(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply)) +{ + AEDescList docList; + AEKeyword keywd; + DescType returnedType; + Size actualSize; + long itemsInList; + OSErr err; + short i; + + err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList,&docList); + if (err != noErr) + return err; + + err = AECountItems(&docList, &itemsInList); + if (err != noErr) + return err; + + ProcessSerialNumber PSN ; + PSN.highLongOfPSN = 0 ; + PSN.lowLongOfPSN = kCurrentProcess ; + SetFrontProcess( &PSN ) ; + + wxString fName ; + FSRef theRef ; + + for (i = 1; i <= itemsInList; i++) + { + AEGetNthPtr( + &docList, i, typeFSRef, &keywd, &returnedType, + (Ptr)&theRef, sizeof(theRef), &actualSize); + fName = wxMacFSRefToPath( &theRef ) ; + + MacOpenFile(fName); + } + + return noErr; +} + +// AEODoc Calls MacOpenURL on the url passed + +short wxApp::MacHandleAEGURL(const WXEVENTREF event, WXEVENTREF WXUNUSED(reply)) +{ + DescType returnedType; + Size actualSize; + char url[255]; + OSErr err = AEGetParamPtr((AppleEvent *)event, keyDirectObject, typeChar, + &returnedType, url, sizeof(url)-1, + &actualSize); + if (err != noErr) + return err; + + url[actualSize] = '\0'; // Terminate the C string + + ProcessSerialNumber PSN ; + PSN.highLongOfPSN = 0 ; + PSN.lowLongOfPSN = kCurrentProcess ; + SetFrontProcess( &PSN ) ; + + MacOpenURL(wxString(url, wxConvUTF8)); + + return noErr; +} + +// AEPDoc Calls MacPrintFile on each of the files passed + +short wxApp::MacHandleAEPDoc(const WXEVENTREF event , WXEVENTREF WXUNUSED(reply)) +{ + AEDescList docList; + AEKeyword keywd; + DescType returnedType; + Size actualSize; + long itemsInList; + OSErr err; + short i; + + err = AEGetParamDesc((AppleEvent *)event, keyDirectObject, typeAEList,&docList); + if (err != noErr) + return err; + + err = AECountItems(&docList, &itemsInList); + if (err != noErr) + return err; + + ProcessSerialNumber PSN ; + PSN.highLongOfPSN = 0 ; + PSN.lowLongOfPSN = kCurrentProcess ; + SetFrontProcess( &PSN ) ; + + wxString fName ; + FSRef theRef ; + + for (i = 1; i <= itemsInList; i++) + { + AEGetNthPtr( + &docList, i, typeFSRef, &keywd, &returnedType, + (Ptr)&theRef, sizeof(theRef), &actualSize); + fName = wxMacFSRefToPath( &theRef ) ; + + MacPrintFile(fName); + } + + return noErr; +} + +// AEOApp calls MacNewFile + +short wxApp::MacHandleAEOApp(const WXEVENTREF WXUNUSED(event) , WXEVENTREF WXUNUSED(reply)) +{ + MacNewFile() ; + return noErr ; +} + +// AEQuit attempts to quit the application + +short wxApp::MacHandleAEQuit(const WXEVENTREF WXUNUSED(event) , WXEVENTREF WXUNUSED(reply)) +{ + wxWindow* win = GetTopWindow() ; + if ( win ) + { + wxCommandEvent exitEvent(wxEVT_COMMAND_MENU_SELECTED, s_macExitMenuItemId); + if (!win->ProcessEvent(exitEvent)) + win->Close(true) ; + } + else + { + ExitMainLoop() ; + } + + return noErr ; +} + +// AEROApp calls MacReopenApp + +short wxApp::MacHandleAERApp(const WXEVENTREF WXUNUSED(event) , WXEVENTREF WXUNUSED(reply)) +{ + MacReopenApp() ; + + return noErr ; +} + +//---------------------------------------------------------------------- +// Support Routines linking the Mac...File Calls to the Document Manager +//---------------------------------------------------------------------- + +void wxApp::MacOpenFile(const wxString & fileName ) +{ +#if wxUSE_DOC_VIEW_ARCHITECTURE + wxDocManager* dm = wxDocManager::GetDocumentManager() ; + if ( dm ) + dm->CreateDocument(fileName , wxDOC_SILENT ) ; +#endif +} + +void wxApp::MacOpenURL(const wxString & WXUNUSED(url) ) +{ +} + +void wxApp::MacPrintFile(const wxString & fileName ) +{ +#if wxUSE_DOC_VIEW_ARCHITECTURE + +#if wxUSE_PRINTING_ARCHITECTURE + wxDocManager* dm = wxDocManager::GetDocumentManager() ; + if ( dm ) + { + wxDocument *doc = dm->CreateDocument(fileName , wxDOC_SILENT ) ; + if ( doc ) + { + wxView* view = doc->GetFirstView() ; + if ( view ) + { + wxPrintout *printout = view->OnCreatePrintout(); + if (printout) + { + wxPrinter printer; + printer.Print(view->GetFrame(), printout, true); + delete printout; + } + } + + if (doc->Close()) + { + doc->DeleteAllViews(); + dm->RemoveDocument(doc) ; + } + } + } +#endif //print + +#endif //docview +} + + + +void wxApp::MacNewFile() +{ +} + +void wxApp::MacReopenApp() +{ + // HIG says : + // if there is no open window -> create a new one + // if all windows are hidden -> show the first + // if some windows are not hidden -> do nothing + + wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst(); + if ( node == NULL ) + { + MacNewFile() ; + } + else + { + wxTopLevelWindow* firstIconized = NULL ; + wxTopLevelWindow* firstHidden = NULL ; + while (node) + { + wxTopLevelWindow* win = (wxTopLevelWindow*) node->GetData(); + if ( !win->IsShown() ) + { + // make sure we don't show 'virtual toplevel windows' like wxTaskBarIconWindow + if ( firstHidden == NULL && ( wxDynamicCast( win, wxFrame ) || wxDynamicCast( win, wxDialog ) ) ) + firstHidden = win ; + } + else if ( win->IsIconized() ) + { + if ( firstIconized == NULL ) + firstIconized = win ; + } + else + { + // we do have a visible, non-iconized toplevelwindow -> do nothing + return; + } + + node = node->GetNext(); + } + + if ( firstIconized ) + firstIconized->Iconize( false ) ; + else if ( firstHidden ) + firstHidden->Show( true ); + } +} + +//---------------------------------------------------------------------- +// Macintosh CommandID support - converting between native and wx IDs +//---------------------------------------------------------------------- + +// if no native match they just return the passed-in id + +struct IdPair +{ + UInt32 macId ; + int wxId ; +} ; + +IdPair gCommandIds [] = +{ + { kHICommandCut , wxID_CUT } , + { kHICommandCopy , wxID_COPY } , + { kHICommandPaste , wxID_PASTE } , + { kHICommandSelectAll , wxID_SELECTALL } , + { kHICommandClear , wxID_CLEAR } , + { kHICommandUndo , wxID_UNDO } , + { kHICommandRedo , wxID_REDO } , +} ; + +int wxMacCommandToId( UInt32 macCommandId ) +{ + int wxid = 0 ; + + switch ( macCommandId ) + { + case kHICommandPreferences : + wxid = wxApp::s_macPreferencesMenuItemId ; + break ; + + case kHICommandQuit : + wxid = wxApp::s_macExitMenuItemId ; + break ; + + case kHICommandAbout : + wxid = wxApp::s_macAboutMenuItemId ; + break ; + + default : + { + for ( size_t i = 0 ; i < WXSIZEOF(gCommandIds) ; ++i ) + { + if ( gCommandIds[i].macId == macCommandId ) + { + wxid = gCommandIds[i].wxId ; + break ; + } + } + } + break ; + } + + if ( wxid == 0 ) + wxid = (int) macCommandId ; + + return wxid ; +} + +UInt32 wxIdToMacCommand( int wxId ) +{ + UInt32 macId = 0 ; + + if ( wxId == wxApp::s_macPreferencesMenuItemId ) + macId = kHICommandPreferences ; + else if (wxId == wxApp::s_macExitMenuItemId) + macId = kHICommandQuit ; + else if (wxId == wxApp::s_macAboutMenuItemId) + macId = kHICommandAbout ; + else + { + for ( size_t i = 0 ; i < WXSIZEOF(gCommandIds) ; ++i ) + { + if ( gCommandIds[i].wxId == wxId ) + { + macId = gCommandIds[i].macId ; + break ; + } + } + } + + if ( macId == 0 ) + macId = (int) wxId ; + + return macId ; +} + +wxMenu* wxFindMenuFromMacCommand( const HICommand &command , wxMenuItem* &item ) +{ + wxMenu* itemMenu = NULL ; +#ifndef __WXUNIVERSAL__ + int id = 0 ; + + // for 'standard' commands which don't have a wx-menu + if ( command.commandID == kHICommandPreferences || command.commandID == kHICommandQuit || command.commandID == kHICommandAbout ) + { + id = wxMacCommandToId( command.commandID ) ; + + wxMenuBar* mbar = wxMenuBar::MacGetInstalledMenuBar() ; + if ( mbar ) + item = mbar->FindItem( id , &itemMenu ) ; + } + else if ( command.commandID != 0 && command.menu.menuRef != 0 && command.menu.menuItemIndex != 0 ) + { + id = wxMacCommandToId( command.commandID ) ; + // make sure it is one of our own menus, or of the 'synthetic' apple and help menus , otherwise don't touch + MenuItemIndex firstUserHelpMenuItem ; + static MenuHandle helpMenuHandle = NULL ; + if ( helpMenuHandle == NULL ) + { + if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) != noErr ) + helpMenuHandle = NULL ; + } + + // is it part of the application or the Help menu, then look for the id directly + if ( ( GetMenuHandle( kwxMacAppleMenuId ) != NULL && command.menu.menuRef == GetMenuHandle( kwxMacAppleMenuId ) ) || + ( helpMenuHandle != NULL && command.menu.menuRef == helpMenuHandle ) || + wxMenuBar::MacGetWindowMenuHMenu() != NULL && command.menu.menuRef == wxMenuBar::MacGetWindowMenuHMenu() ) + { + wxMenuBar* mbar = wxMenuBar::MacGetInstalledMenuBar() ; + if ( mbar ) + item = mbar->FindItem( id , &itemMenu ) ; + } + else + { + URefCon refCon ; + + GetMenuItemRefCon( command.menu.menuRef , command.menu.menuItemIndex , &refCon ) ; + itemMenu = wxFindMenuFromMacMenu( command.menu.menuRef ) ; + if ( itemMenu != NULL ) + item = (wxMenuItem*) refCon ; + } + } +#endif + return itemMenu ; +} + +//---------------------------------------------------------------------- +// Carbon Event Handler +//---------------------------------------------------------------------- + +static const EventTypeSpec eventList[] = +{ + { kEventClassCommand, kEventProcessCommand } , + { kEventClassCommand, kEventCommandUpdateStatus } , + + { kEventClassMenu, kEventMenuOpening }, + { kEventClassMenu, kEventMenuClosed }, + { kEventClassMenu, kEventMenuTargetItem }, + + { kEventClassApplication , kEventAppActivated } , + { kEventClassApplication , kEventAppDeactivated } , + // handling the quit event is not recommended by apple + // rather using the quit apple event - which we do + + { kEventClassAppleEvent , kEventAppleEvent } , + + { kEventClassMouse , kEventMouseDown } , + { kEventClassMouse , kEventMouseMoved } , + { kEventClassMouse , kEventMouseUp } , + { kEventClassMouse , kEventMouseDragged } , + { 'WXMC' , 'WXMC' } +} ; + +static pascal OSStatus +wxMacAppMenuEventHandler( EventHandlerCallRef WXUNUSED(handler), + EventRef event, + void *WXUNUSED(data) ) +{ + wxMacCarbonEvent cEvent( event ) ; + MenuRef menuRef = cEvent.GetParameter(kEventParamDirectObject) ; +#ifndef __WXUNIVERSAL__ + wxMenu* menu = wxFindMenuFromMacMenu( menuRef ) ; + + if ( menu ) + { + wxEventType type=0; + MenuCommand cmd=0; + switch (GetEventKind(event)) + { + case kEventMenuOpening: + type = wxEVT_MENU_OPEN; + break; + + case kEventMenuClosed: + type = wxEVT_MENU_CLOSE; + break; + + case kEventMenuTargetItem: + cmd = cEvent.GetParameter(kEventParamMenuCommand,typeMenuCommand) ; + if (cmd != 0) + type = wxEVT_MENU_HIGHLIGHT; + break; + + default: + wxFAIL_MSG(wxT("Unexpected menu event kind")); + break; + } + + if ( type ) + { + wxMenuEvent wxevent(type, cmd, menu); + wxevent.SetEventObject(menu); + + wxEvtHandler* handler = menu->GetEventHandler(); + if (handler && handler->ProcessEvent(wxevent)) + { + // handled + } + else + { + wxWindow *win = menu->GetInvokingWindow(); + if (win) + win->HandleWindowEvent(wxevent); + } + } + } +#endif + return eventNotHandledErr; +} + +#ifndef __LP64__ +static pascal OSStatus +wxMacAppCommandEventHandler( EventHandlerCallRef WXUNUSED(handler) , + EventRef event , + void *WXUNUSED(data) ) +{ + OSStatus result = eventNotHandledErr ; + + HICommand command ; + + wxMacCarbonEvent cEvent( event ) ; + cEvent.GetParameter(kEventParamDirectObject,typeHICommand,&command) ; + + wxMenuItem* item = NULL ; + wxMenu* itemMenu = wxFindMenuFromMacCommand( command , item ) ; + int id = wxMacCommandToId( command.commandID ) ; + + if ( item ) + { + wxASSERT( itemMenu != NULL ) ; + + switch ( cEvent.GetKind() ) + { + case kEventProcessCommand : + result = itemMenu->MacHandleCommandProcess( item, id ); + break ; + + case kEventCommandUpdateStatus: + result = itemMenu->MacHandleCommandUpdateStatus( item, id ); + break ; + + default : + break ; + } + } + return result ; +} +#endif + +static pascal OSStatus +wxMacAppApplicationEventHandler( EventHandlerCallRef WXUNUSED(handler) , + EventRef event , + void *WXUNUSED(data) ) +{ + OSStatus result = eventNotHandledErr ; + switch ( GetEventKind( event ) ) + { + case kEventAppActivated : + if ( wxTheApp ) + wxTheApp->SetActive( true , NULL ) ; + result = noErr ; + break ; + + case kEventAppDeactivated : + if ( wxTheApp ) + wxTheApp->SetActive( false , NULL ) ; + result = noErr ; + break ; + + default : + break ; + } + + return result ; +} + +pascal OSStatus wxMacAppEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + EventRef formerEvent = (EventRef) wxTheApp->MacGetCurrentEvent() ; + EventHandlerCallRef formerEventHandlerCallRef = (EventHandlerCallRef) wxTheApp->MacGetCurrentEventHandlerCallRef() ; + wxTheApp->MacSetCurrentEvent( event , handler ) ; + + OSStatus result = eventNotHandledErr ; + switch ( GetEventClass( event ) ) + { +#ifndef __LP64__ + case kEventClassCommand : + result = wxMacAppCommandEventHandler( handler , event , data ) ; + break ; +#endif + case kEventClassApplication : + result = wxMacAppApplicationEventHandler( handler , event , data ) ; + break ; +#ifndef __LP64__ + case kEventClassMenu : + result = wxMacAppMenuEventHandler( handler , event , data ) ; + break ; + + case kEventClassMouse : + { + wxMacCarbonEvent cEvent( event ) ; + + WindowRef window ; + Point screenMouseLocation = cEvent.GetParameter(kEventParamMouseLocation) ; + ::FindWindow(screenMouseLocation, &window); + // only send this event in case it had not already been sent to a tlw, as we get + // double events otherwise (in case event.skip) was called + if ( window == NULL ) + result = wxMacTopLevelMouseEventHandler( handler , event , NULL ) ; + } + break ; +#endif + case kEventClassAppleEvent : + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( AEProcessEvent != NULL ) + { + result = AEProcessEvent(event); + } +#endif +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + { + EventRecord rec ; + + wxMacConvertEventToRecord( event , &rec ) ; + result = AEProcessAppleEvent( &rec ) ; + } +#endif + } + break ; + + default : + break ; + } + + wxTheApp->MacSetCurrentEvent( formerEvent, formerEventHandlerCallRef ) ; + + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacAppEventHandler ) + +#ifdef __WXDEBUG__ + +pascal static void +wxMacAssertOutputHandler(OSType WXUNUSED(componentSignature), + UInt32 WXUNUSED(options), + const char *assertionString, + const char *exceptionLabelString, + const char *errorString, + const char *fileName, + long lineNumber, + void *value, + ConstStr255Param WXUNUSED(outputMsg)) +{ + // flow into assert handling + wxString fileNameStr ; + wxString assertionStr ; + wxString exceptionStr ; + wxString errorStr ; + +#if wxUSE_UNICODE + fileNameStr = wxString(fileName, wxConvLocal); + assertionStr = wxString(assertionString, wxConvLocal); + exceptionStr = wxString((exceptionLabelString!=0) ? exceptionLabelString : "", wxConvLocal) ; + errorStr = wxString((errorString!=0) ? errorString : "", wxConvLocal) ; +#else + fileNameStr = fileName; + assertionStr = assertionString; + exceptionStr = (exceptionLabelString!=0) ? exceptionLabelString : "" ; + errorStr = (errorString!=0) ? errorString : "" ; +#endif + +#if 1 + // flow into log + wxLogDebug( wxT("AssertMacros: %s %s %s file: %s, line: %ld (value %p)\n"), + assertionStr.c_str() , + exceptionStr.c_str() , + errorStr.c_str(), + fileNameStr.c_str(), lineNumber , + value ) ; +#else + + wxOnAssert(fileNameStr, lineNumber , assertionStr , + wxString::Format( wxT("%s %s value (%p)") , exceptionStr, errorStr , value ) ) ; +#endif +} + +#endif //__WXDEBUG__ + +extern "C" void macPostedEventCallback(void *WXUNUSED(unused)) +{ + wxTheApp->ProcessPendingEvents(); +} + +bool wxApp::Initialize(int& argc, wxChar **argv) +{ + // Mac-specific + +#ifdef __WXDEBUG__ + InstallDebugAssertOutputHandler( NewDebugAssertOutputHandlerUPP( wxMacAssertOutputHandler ) ); +#endif + + UMAInitToolbox( 4, sm_isEmbedded ) ; +// TODO CHECK Can Be Removed SetEventMask( everyEvent ) ; + + // Mac OS X passes a process serial number command line argument when + // the application is launched from the Finder. This argument must be + // removed from the command line arguments before being handled by the + // application (otherwise applications would need to handle it) + if ( argc > 1 ) + { + static const wxChar *ARG_PSN = _T("-psn_"); + if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 ) + { + // remove this argument + --argc; + memmove(argv + 1, argv + 2, argc * sizeof(char *)); + } + } + + if ( !wxAppBase::Initialize(argc, argv) ) + return false; + +#if wxUSE_INTL + wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding()); +#endif + + // these might be the startup dirs, set them to the 'usual' dir containing the app bundle + wxString startupCwd = wxGetCwd() ; + if ( startupCwd == wxT("/") || startupCwd.Right(15) == wxT("/Contents/MacOS") ) + { + CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle() ) ; + CFURLRef urlParent = CFURLCreateCopyDeletingLastPathComponent( kCFAllocatorDefault , url ) ; + CFRelease( url ) ; + CFStringRef path = CFURLCopyFileSystemPath ( urlParent , kCFURLPOSIXPathStyle ) ; + CFRelease( urlParent ) ; + wxString cwd = wxCFStringRef(path).AsString(wxLocale::GetSystemEncoding()); + wxSetWorkingDirectory( cwd ) ; + } + + /* connect posted events to common-mode run loop so that wxPostEvent events + are handled even while we're in the menu or on a scrollbar */ + CFRunLoopSourceContext event_posted_context = {0}; + event_posted_context.perform = macPostedEventCallback; + m_macEventPosted = CFRunLoopSourceCreate(NULL,0,&event_posted_context); + CFRunLoopAddSource(CFRunLoopGetCurrent(), m_macEventPosted, kCFRunLoopCommonModes); + // run loop takes ownership + CFRelease(m_macEventPosted); + + return true; +} + +AEEventHandlerUPP sODocHandler = NULL ; +AEEventHandlerUPP sGURLHandler = NULL ; +AEEventHandlerUPP sOAppHandler = NULL ; +AEEventHandlerUPP sPDocHandler = NULL ; +AEEventHandlerUPP sRAppHandler = NULL ; +AEEventHandlerUPP sQuitHandler = NULL ; + +bool wxApp::OnInitGui() +{ + if ( !wxAppBase::OnInitGui() ) + return false ; +#ifndef __LP64__ + InstallStandardEventHandler( GetApplicationEventTarget() ) ; + if (!sm_isEmbedded) + { + InstallApplicationEventHandler( + GetwxMacAppEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, wxTheApp, (EventHandlerRef *)&(wxTheApp->m_macEventHandler)); + } +#endif + + if (!sm_isEmbedded) + { + sODocHandler = NewAEEventHandlerUPP(AEHandleODoc) ; + sGURLHandler = NewAEEventHandlerUPP(AEHandleGURL) ; + sOAppHandler = NewAEEventHandlerUPP(AEHandleOApp) ; + sPDocHandler = NewAEEventHandlerUPP(AEHandlePDoc) ; + sRAppHandler = NewAEEventHandlerUPP(AEHandleRApp) ; + sQuitHandler = NewAEEventHandlerUPP(AEHandleQuit) ; + + AEInstallEventHandler( kCoreEventClass , kAEOpenDocuments , + sODocHandler , 0 , FALSE ) ; + AEInstallEventHandler( kInternetEventClass, kAEGetURL, + sGURLHandler , 0 , FALSE ) ; + AEInstallEventHandler( kCoreEventClass , kAEOpenApplication , + sOAppHandler , 0 , FALSE ) ; + AEInstallEventHandler( kCoreEventClass , kAEPrintDocuments , + sPDocHandler , 0 , FALSE ) ; + AEInstallEventHandler( kCoreEventClass , kAEReopenApplication , + sRAppHandler , 0 , FALSE ) ; + AEInstallEventHandler( kCoreEventClass , kAEQuitApplication , + sQuitHandler , 0 , FALSE ) ; + } + + if ( !wxMacInitCocoa() ) + return false; + + return true ; +} + +void wxApp::CleanUp() +{ +#if wxUSE_TOOLTIPS + wxToolTip::RemoveToolTips() ; +#endif + + if (m_macEventPosted) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_macEventPosted, kCFRunLoopCommonModes); + m_macEventPosted = NULL; + } + + // One last chance for pending objects to be cleaned up + wxTheApp->DeletePendingObjects(); + + if (!sm_isEmbedded) + RemoveEventHandler( (EventHandlerRef)(wxTheApp->m_macEventHandler) ); + + if (!sm_isEmbedded) + { + AERemoveEventHandler( kCoreEventClass , kAEOpenDocuments , + sODocHandler , FALSE ) ; + AERemoveEventHandler( kInternetEventClass, kAEGetURL, + sGURLHandler , FALSE ) ; + AERemoveEventHandler( kCoreEventClass , kAEOpenApplication , + sOAppHandler , FALSE ) ; + AERemoveEventHandler( kCoreEventClass , kAEPrintDocuments , + sPDocHandler , FALSE ) ; + AERemoveEventHandler( kCoreEventClass , kAEReopenApplication , + sRAppHandler , FALSE ) ; + AERemoveEventHandler( kCoreEventClass , kAEQuitApplication , + sQuitHandler , FALSE ) ; + + DisposeAEEventHandlerUPP( sODocHandler ) ; + DisposeAEEventHandlerUPP( sGURLHandler ) ; + DisposeAEEventHandlerUPP( sOAppHandler ) ; + DisposeAEEventHandlerUPP( sPDocHandler ) ; + DisposeAEEventHandlerUPP( sRAppHandler ) ; + DisposeAEEventHandlerUPP( sQuitHandler ) ; + } + + wxAppBase::CleanUp(); +} + +//---------------------------------------------------------------------- +// misc initialization stuff +//---------------------------------------------------------------------- + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 +bool wxMacConvertEventToRecord( EventRef event , EventRecord *rec) +{ + OSStatus err = noErr ; + bool converted = ConvertEventRefToEventRecord( event, rec) ; + + if ( !converted ) + { + switch ( GetEventClass( event ) ) + { + case kEventClassKeyboard : + { + converted = true ; + switch ( GetEventKind(event) ) + { + case kEventRawKeyDown : + rec->what = keyDown ; + break ; + + case kEventRawKeyRepeat : + rec->what = autoKey ; + break ; + + case kEventRawKeyUp : + rec->what = keyUp ; + break ; + + case kEventRawKeyModifiersChanged : + rec->what = nullEvent ; + break ; + + default : + converted = false ; + break ; + } + + if ( converted ) + { + UInt32 keyCode ; + unsigned char charCode ; + UInt32 modifiers ; + GetMouse( &rec->where) ; + err = GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, 4, NULL, &modifiers); + err = GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, 4, NULL, &keyCode); + err = GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, 1, NULL, &charCode); + rec->modifiers = modifiers ; + rec->message = (keyCode << 8 ) + charCode ; + } + } + break ; + + case kEventClassTextInput : + { + switch ( GetEventKind( event ) ) + { + case kEventTextInputUnicodeForKeyEvent : + { + EventRef rawEvent ; + err = GetEventParameter( + event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, + sizeof(rawEvent), NULL, &rawEvent ) ; + converted = true ; + + { + UInt32 keyCode, modifiers; + unsigned char charCode ; + GetMouse( &rec->where) ; + rec->what = keyDown ; + err = GetEventParameter(rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, 4, NULL, &modifiers); + err = GetEventParameter(rawEvent, kEventParamKeyCode, typeUInt32, NULL, 4, NULL, &keyCode); + err = GetEventParameter(rawEvent, kEventParamKeyMacCharCodes, typeChar, NULL, 1, NULL, &charCode); + rec->modifiers = modifiers ; + rec->message = (keyCode << 8 ) + charCode ; + } + } + break ; + + default : + break ; + } + } + break ; + + default : + break ; + } + } + + return converted ; +} +#endif + +wxApp::wxApp() +{ + m_printMode = wxPRINT_WINDOWS; + + m_macCurrentEvent = NULL ; + m_macCurrentEventHandlerCallRef = NULL ; + m_macEventPosted = NULL ; +} + +void wxApp::OnIdle(wxIdleEvent& WXUNUSED(event)) +{ + // If they are pending events, we must process them: pending events are + // either events to the threads other than main or events posted with + // wxPostEvent() functions +#ifndef __WXUNIVERSAL__ + if (!wxMenuBar::MacGetInstalledMenuBar() && wxMenuBar::MacGetCommonMenuBar()) + wxMenuBar::MacGetCommonMenuBar()->MacInstallMenuBar(); +#endif +} + +void wxApp::WakeUpIdle() +{ + if (m_macEventPosted) + { + CFRunLoopSourceSignal(m_macEventPosted); + } + + wxMacWakeUp() ; +} + +void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event)) +{ + if (GetTopWindow()) + GetTopWindow()->Close(true); +} + +// Default behaviour: close the application with prompts. The +// user can veto the close, and therefore the end session. +void wxApp::OnQueryEndSession(wxCloseEvent& event) +{ + if (GetTopWindow()) + { + if (!GetTopWindow()->Close(!event.CanVeto())) + event.Veto(true); + } +} + +extern "C" void wxCYield() ; +void wxCYield() +{ + wxYield() ; +} + +// Yield to other processes + +bool wxApp::Yield(bool onlyIfNeeded) +{ + if (s_inYield) + { + if ( !onlyIfNeeded ) + { + wxFAIL_MSG( wxT("wxYield called recursively" ) ); + } + + return false; + } + +#if wxUSE_THREADS + // Yielding from a non-gui thread needs to bail out, otherwise we end up + // possibly sending events in the thread too. + if ( !wxThread::IsMain() ) + { + return true; + } +#endif // wxUSE_THREADS + + s_inYield = true; + + // by definition yield should handle all non-processed events + + EventRef theEvent; + + OSStatus status = noErr ; + + while ( status == noErr ) + { + s_inReceiveEvent = true ; + status = ReceiveNextEvent(0, NULL,kEventDurationNoWait,true,&theEvent) ; + s_inReceiveEvent = false ; + + if ( status == eventLoopTimedOutErr ) + { + // make sure next time the event loop will trigger idle events + sleepTime = kEventDurationNoWait ; + } + else if ( status == eventLoopQuitErr ) + { + // according to QA1061 this may also occur when a WakeUp Process + // is executed + } + else + { + MacHandleOneEvent( theEvent ) ; + ReleaseEvent(theEvent); + } + } + + s_inYield = false; + + return true; +} + +void wxApp::MacDoOneEvent() +{ + wxMacAutoreleasePool autoreleasepool; + EventRef theEvent; + + s_inReceiveEvent = true ; + OSStatus status = ReceiveNextEvent(0, NULL, sleepTime, true, &theEvent) ; + s_inReceiveEvent = false ; + + switch (status) + { + case eventLoopTimedOutErr : + if ( wxTheApp->ProcessIdle() ) + sleepTime = kEventDurationNoWait ; + else + sleepTime = kEventDurationSecond; + break; + + case eventLoopQuitErr : + // according to QA1061 this may also occur + // when a WakeUp Process is executed + break; + + default: + MacHandleOneEvent( theEvent ) ; + ReleaseEvent( theEvent ); + sleepTime = kEventDurationNoWait ; + break; + } + // repeaters + + DeletePendingObjects() ; +} + +// virtual +void wxApp::MacHandleUnhandledEvent( WXEVENTREF WXUNUSED(evr) ) +{ + // Override to process unhandled events as you please +} + +CFMutableArrayRef GetAutoReleaseArray() +{ + static CFMutableArrayRef array = 0; + if ( array == 0) + array= CFArrayCreateMutable(kCFAllocatorDefault,0,&kCFTypeArrayCallBacks); + return array; +} + +void wxApp::MacHandleOneEvent( WXEVENTREF evr ) +{ + EventTargetRef theTarget; + theTarget = GetEventDispatcherTarget(); + m_macCurrentEvent = evr ; + + OSStatus status = SendEventToEventTarget((EventRef) evr , theTarget); + if (status == eventNotHandledErr) + MacHandleUnhandledEvent(evr); + +#if wxUSE_THREADS + wxMutexGuiLeaveOrEnter(); +#endif // wxUSE_THREADS + + CFArrayRemoveAllValues( GetAutoReleaseArray() ); +} + +void wxApp::MacAddToAutorelease( void* cfrefobj ) +{ + CFArrayAppendValue( GetAutoReleaseArray(), cfrefobj ); +} + +long wxMacTranslateKey(unsigned char key, unsigned char code) +{ + long retval = key ; + switch (key) + { + case kHomeCharCode : + retval = WXK_HOME; + break; + + case kEnterCharCode : + retval = WXK_RETURN; + break; + case kEndCharCode : + retval = WXK_END; + break; + + case kHelpCharCode : + retval = WXK_HELP; + break; + + case kBackspaceCharCode : + retval = WXK_BACK; + break; + + case kTabCharCode : + retval = WXK_TAB; + break; + + case kPageUpCharCode : + retval = WXK_PAGEUP; + break; + + case kPageDownCharCode : + retval = WXK_PAGEDOWN; + break; + + case kReturnCharCode : + retval = WXK_RETURN; + break; + + case kFunctionKeyCharCode : + { + switch ( code ) + { + case 0x7a : + retval = WXK_F1 ; + break; + + case 0x78 : + retval = WXK_F2 ; + break; + + case 0x63 : + retval = WXK_F3 ; + break; + + case 0x76 : + retval = WXK_F4 ; + break; + + case 0x60 : + retval = WXK_F5 ; + break; + + case 0x61 : + retval = WXK_F6 ; + break; + + case 0x62: + retval = WXK_F7 ; + break; + + case 0x64 : + retval = WXK_F8 ; + break; + + case 0x65 : + retval = WXK_F9 ; + break; + + case 0x6D : + retval = WXK_F10 ; + break; + + case 0x67 : + retval = WXK_F11 ; + break; + + case 0x6F : + retval = WXK_F12 ; + break; + + case 0x69 : + retval = WXK_F13 ; + break; + + case 0x6B : + retval = WXK_F14 ; + break; + + case 0x71 : + retval = WXK_F15 ; + break; + + default: + break; + } + } + break ; + + case kEscapeCharCode : + retval = WXK_ESCAPE ; + break ; + + case kLeftArrowCharCode : + retval = WXK_LEFT ; + break ; + + case kRightArrowCharCode : + retval = WXK_RIGHT ; + break ; + + case kUpArrowCharCode : + retval = WXK_UP ; + break ; + + case kDownArrowCharCode : + retval = WXK_DOWN ; + break ; + + case kDeleteCharCode : + retval = WXK_DELETE ; + break ; + + default: + break ; + } // end switch + + return retval; +} + +int wxMacKeyCodeToModifier(wxKeyCode key) +{ + switch (key) + { + case WXK_START: + case WXK_MENU: + return cmdKey; + + case WXK_SHIFT: + return shiftKey; + + case WXK_CAPITAL: + return alphaLock; + + case WXK_ALT: + return optionKey; + + case WXK_CONTROL: + return controlKey; + + default: + return 0; + } +} + +wxMouseState wxGetMouseState() +{ + wxMouseState ms; + + wxPoint pt = wxGetMousePosition(); + ms.SetX(pt.x); + ms.SetY(pt.y); + + UInt32 buttons = GetCurrentButtonState(); + ms.SetLeftDown( (buttons & 0x01) != 0 ); + ms.SetMiddleDown( (buttons & 0x04) != 0 ); + ms.SetRightDown( (buttons & 0x02) != 0 ); + + UInt32 modifiers = GetCurrentKeyModifiers(); + ms.SetControlDown(modifiers & controlKey); + ms.SetShiftDown(modifiers & shiftKey); + ms.SetAltDown(modifiers & optionKey); + ms.SetMetaDown(modifiers & cmdKey); + + return ms; +} + +// TODO : once the new key/char handling is tested, move all the code to wxWindow + +bool wxApp::MacSendKeyDownEvent( wxWindow* focus , long keymessage , long modifiers , long when , short wherex , short wherey , wxChar uniChar ) +{ + if ( !focus ) + return false ; + + bool handled; + wxKeyEvent event(wxEVT_KEY_DOWN) ; + MacCreateKeyEvent( event, focus , keymessage , modifiers , when , wherex , wherey , uniChar ) ; + + handled = focus->HandleWindowEvent( event ) ; + if ( handled && event.GetSkipped() ) + handled = false ; + +#if wxUSE_ACCEL + if ( !handled ) + { + wxWindow *ancestor = focus; + while (ancestor) + { + int command = ancestor->GetAcceleratorTable()->GetCommand( event ); + if (command != -1) + { + wxEvtHandler * const handler = ancestor->GetEventHandler(); + + wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command ); + handled = handler->ProcessEvent( command_event ); + + if ( !handled ) + { + // accelerators can also be used with buttons, try them too + command_event.SetEventType(wxEVT_COMMAND_BUTTON_CLICKED); + handled = handler->ProcessEvent( command_event ); + } + + break; + } + + if (ancestor->IsTopLevel()) + break; + + ancestor = ancestor->GetParent(); + } + } +#endif // wxUSE_ACCEL + + return handled ; +} + +bool wxApp::MacSendKeyUpEvent( wxWindow* focus , long keymessage , long modifiers , long when , short wherex , short wherey , wxChar uniChar ) +{ + if ( !focus ) + return false ; + + bool handled; + wxKeyEvent event( wxEVT_KEY_UP ) ; + MacCreateKeyEvent( event, focus , keymessage , modifiers , when , wherex , wherey , uniChar ) ; + handled = focus->HandleWindowEvent( event ) ; + + return handled ; +} + +bool wxApp::MacSendCharEvent( wxWindow* focus , long keymessage , long modifiers , long when , short wherex , short wherey , wxChar uniChar ) +{ + if ( !focus ) + return false ; + + wxKeyEvent event(wxEVT_CHAR) ; + MacCreateKeyEvent( event, focus , keymessage , modifiers , when , wherex , wherey , uniChar ) ; + long keyval = event.m_keyCode ; + + bool handled = false ; + + wxNonOwnedWindow *tlw = focus->MacGetTopLevelWindow() ; + + if (tlw) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + handled = tlw->HandleWindowEvent( event ); + if ( handled && event.GetSkipped() ) + handled = false ; + } + + if ( !handled ) + { + event.SetEventType( wxEVT_CHAR ); + event.Skip( false ) ; + handled = focus->HandleWindowEvent( event ) ; + } + + if ( !handled && (keyval == WXK_TAB) ) + { + wxWindow* iter = focus->GetParent() ; + while ( iter && !handled ) + { + if ( iter->HasFlag( wxTAB_TRAVERSAL ) ) + { + wxNavigationKeyEvent new_event; + new_event.SetEventObject( focus ); + new_event.SetDirection( !event.ShiftDown() ); + /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */ + new_event.SetWindowChange( event.ControlDown() ); + new_event.SetCurrentFocus( focus ); + handled = focus->GetParent()->HandleWindowEvent( new_event ); + if ( handled && new_event.GetSkipped() ) + handled = false ; + } + + iter = iter->GetParent() ; + } + } + + // backdoor handler for default return and command escape + if ( !handled && (!focus->IsKindOf(CLASSINFO(wxControl) ) || !focus->MacCanFocus() ) ) + { + // if window is not having a focus still testing for default enter or cancel + // TODO: add the UMA version for ActiveNonFloatingWindow +#ifndef __LP64__ + wxWindow* focus = wxFindWinFromMacWindow( FrontWindow() ) ; + if ( focus ) + { + if ( keyval == WXK_RETURN || keyval == WXK_NUMPAD_ENTER ) + { + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(focus), wxTopLevelWindow); + if ( tlw && tlw->GetDefaultItem() ) + { + wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton); + if ( def && def->IsEnabled() ) + { + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() ); + event.SetEventObject(def); + def->Command(event); + + return true ; + } + } + } + else if (keyval == WXK_ESCAPE || (keyval == '.' && modifiers & cmdKey ) ) + { + // generate wxID_CANCEL if command-. or has been pressed (typically in dialogs) + wxCommandEvent new_event(wxEVT_COMMAND_BUTTON_CLICKED,wxID_CANCEL); + new_event.SetEventObject( focus ); + handled = focus->HandleWindowEvent( new_event ); + } + } +#endif + } + return handled ; +} + +// This method handles common code for SendKeyDown, SendKeyUp, and SendChar events. +void wxApp::MacCreateKeyEvent( wxKeyEvent& event, wxWindow* focus , long keymessage , long modifiers , long when , short wherex , short wherey , wxChar uniChar ) +{ + short keycode, keychar ; + + keychar = short(keymessage & charCodeMask); + keycode = short(keymessage & keyCodeMask) >> 8 ; + if ( !(event.GetEventType() == wxEVT_CHAR) && (modifiers & (controlKey | shiftKey | optionKey) ) ) + { + // control interferes with some built-in keys like pgdown, return etc. therefore we remove the controlKey modifier + // and look at the character after +#ifdef __LP64__ + // TODO new implementation using TextInputSources +#else + UInt32 state = 0; + UInt32 keyInfo = KeyTranslate((Ptr)GetScriptManagerVariable(smKCHRCache), ( modifiers & (~(controlKey | shiftKey | optionKey))) | keycode, &state); + keychar = short(keyInfo & charCodeMask); +#endif + } + + long keyval = wxMacTranslateKey(keychar, keycode) ; + if ( keyval == keychar && ( event.GetEventType() == wxEVT_KEY_UP || event.GetEventType() == wxEVT_KEY_DOWN ) ) + keyval = wxToupper( keyval ) ; + + // Check for NUMPAD keys. For KEY_UP/DOWN events we need to use the + // WXK_NUMPAD constants, but for the CHAR event we want to use the + // standard ascii values + if ( event.GetEventType() != wxEVT_CHAR ) + { + if (keyval >= '0' && keyval <= '9' && keycode >= 82 && keycode <= 92) + { + keyval = (keyval - '0') + WXK_NUMPAD0; + } + else if (keycode >= 65 && keycode <= 81) + { + switch (keycode) + { + case 76 : + keyval = WXK_NUMPAD_ENTER; + break; + + case 81: + keyval = WXK_NUMPAD_EQUAL; + break; + + case 67: + keyval = WXK_NUMPAD_MULTIPLY; + break; + + case 75: + keyval = WXK_NUMPAD_DIVIDE; + break; + + case 78: + keyval = WXK_NUMPAD_SUBTRACT; + break; + + case 69: + keyval = WXK_NUMPAD_ADD; + break; + + case 65: + keyval = WXK_NUMPAD_DECIMAL; + break; + default: + break; + } + } + } + + event.m_shiftDown = modifiers & shiftKey; + event.m_controlDown = modifiers & controlKey; + event.m_altDown = modifiers & optionKey; + event.m_metaDown = modifiers & cmdKey; + event.m_keyCode = keyval ; +#if wxUSE_UNICODE + event.m_uniChar = uniChar ; +#endif + + event.m_rawCode = keymessage; + event.m_rawFlags = modifiers; + event.m_x = wherex; + event.m_y = wherey; + event.SetTimestamp(when); + event.SetEventObject(focus); +} + + +void wxApp::MacHideApp() +{ + wxMacCarbonEvent event( kEventClassCommand , kEventCommandProcess ); + HICommand command; + memset( &command, 0 , sizeof(command) ); + command.commandID = kHICommandHide ; + event.SetParameter(kEventParamDirectObject, command ); + SendEventToApplication( event ); +} diff --git a/src/osx/carbon/apprsrc.h b/src/osx/carbon/apprsrc.h new file mode 100644 index 0000000000..55d7f307ad --- /dev/null +++ b/src/osx/carbon/apprsrc.h @@ -0,0 +1 @@ +// TODO REMOVE diff --git a/src/osx/carbon/apprsrc.r b/src/osx/carbon/apprsrc.r new file mode 100644 index 0000000000..4113a172a0 --- /dev/null +++ b/src/osx/carbon/apprsrc.r @@ -0,0 +1 @@ +/* not needed anymore */ diff --git a/src/osx/carbon/artmac.cpp b/src/osx/carbon/artmac.cpp new file mode 100644 index 0000000000..466ba9be56 --- /dev/null +++ b/src/osx/carbon/artmac.cpp @@ -0,0 +1,108 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/artmac.cpp +// Purpose: wxArtProvider instance with native Mac stock icons +// Author: Alan Shouls +// Created: 2006-10-30 +// RCS-ID: $Id$ +// Copyright: (c) wxWindows team +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// --------------------------------------------------------------------------- +// headers +// --------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if defined(__BORLANDC__) + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/image.h" +#endif + +#include "wx/artprov.h" +#include "wx/image.h" + +// ---------------------------------------------------------------------------- +// wxMacArtProvider +// ---------------------------------------------------------------------------- + +class wxMacArtProvider : public wxArtProvider +{ +protected: + virtual wxBitmap CreateBitmap(const wxArtID& id, const wxArtClient& client, + const wxSize& size); + virtual wxIconBundle CreateIconBundle(const wxArtID& id, + const wxArtClient& client); +}; + +/* static */ void wxArtProvider::InitNativeProvider() +{ + wxArtProvider::Push(new wxMacArtProvider); +} + +// ---------------------------------------------------------------------------- +// helper macros +// ---------------------------------------------------------------------------- + +#define CREATE_STD_ICON(iconId, xpmRc) \ + { \ + wxIconBundle icon(_T(iconId), wxBITMAP_TYPE_ICON_RESOURCE); \ + return icon; \ + } + +// Macro used in CreateBitmap to get wxICON_FOO icons: +#define ART_MSGBOX(artId, iconId, xpmRc) \ + if ( id == artId ) \ + { \ + CREATE_STD_ICON(#iconId, xpmRc) \ + } + +static wxIconBundle wxMacArtProvider_CreateIconBundle(const wxArtID& id) +{ + ART_MSGBOX(wxART_ERROR, wxICON_ERROR, error) + ART_MSGBOX(wxART_INFORMATION, wxICON_INFORMATION, info) + ART_MSGBOX(wxART_WARNING, wxICON_WARNING, warning) + ART_MSGBOX(wxART_QUESTION, wxICON_QUESTION, question) + + ART_MSGBOX(wxART_FOLDER, wxICON_FOLDER, folder) + ART_MSGBOX(wxART_FOLDER_OPEN, wxICON_FOLDER_OPEN, folder_open) + ART_MSGBOX(wxART_NORMAL_FILE, wxICON_NORMAL_FILE, deffile) + + return wxNullIconBundle; +} + +// ---------------------------------------------------------------------------- +// CreateIconBundle +// ---------------------------------------------------------------------------- + +wxIconBundle wxMacArtProvider::CreateIconBundle(const wxArtID& id, const wxArtClient& client) +{ + // On the Mac folders in lists are always drawn closed, so if an open + // folder icon is asked for we will ask for a closed one in its place + if ( client == wxART_LIST && id == wxART_FOLDER_OPEN ) + return wxMacArtProvider_CreateIconBundle(wxART_FOLDER); + + return wxMacArtProvider_CreateIconBundle(id); +} + +// ---------------------------------------------------------------------------- +// CreateBitmap +// ---------------------------------------------------------------------------- + +wxBitmap wxMacArtProvider::CreateBitmap(const wxArtID& id, + const wxArtClient& client, + const wxSize& reqSize) +{ + wxIconBundle ic(CreateIconBundle(id, client)); + if (ic.IsOk()) + { + wxIcon theIcon(ic.GetIcon(reqSize)); + return wxBitmap(theIcon); + } + + return wxNullBitmap; +} diff --git a/src/osx/carbon/bitmap.cpp b/src/osx/carbon/bitmap.cpp new file mode 100644 index 0000000000..1183eb0fbb --- /dev/null +++ b/src/osx/carbon/bitmap.cpp @@ -0,0 +1,1721 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/bitmap.cpp +// Purpose: wxBitmap +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/bitmap.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/dcmemory.h" + #include "wx/icon.h" + #include "wx/image.h" +#endif + +#include "wx/metafile.h" +#include "wx/xpmdecod.h" + +#include "wx/rawbmp.h" + +IMPLEMENT_DYNAMIC_CLASS(wxBitmap, wxGDIObject) +IMPLEMENT_DYNAMIC_CLASS(wxMask, wxObject) + +#include "wx/mac/private.h" +#ifndef __WXOSX_IPHONE__ +#include +#endif + +CGColorSpaceRef wxMacGetGenericRGBColorSpace(); +CGDataProviderRef wxMacCGDataProviderCreateWithMemoryBuffer( const wxMemoryBuffer& buf ); + +// Implementation Notes +// -------------------- +// +// we are always working with a 32 bit deep pixel buffer +// under QuickDraw its alpha parts are going to be ignored in the GWorld, +// therefore we have a separate GWorld there for blitting the mask in + +// under Quartz then content is transformed into a CGImageRef representing the same data +// which can be transferred to the GPU by the OS for fast rendering + +class WXDLLEXPORT wxBitmapRefData: public wxGDIRefData +{ + friend class WXDLLIMPEXP_FWD_CORE wxIcon; + friend class WXDLLIMPEXP_FWD_CORE wxCursor; +public: + wxBitmapRefData(int width , int height , int depth); + wxBitmapRefData(); + wxBitmapRefData(const wxBitmapRefData &tocopy); + + virtual ~wxBitmapRefData(); + + virtual bool IsOk() const { return m_ok; } + + void Free(); + void SetOk( bool isOk) { m_ok = isOk; } + + void SetWidth( int width ) { m_width = width; } + void SetHeight( int height ) { m_height = height; } + void SetDepth( int depth ) { m_depth = depth; } + + int GetWidth() const { return m_width; } + int GetHeight() const { return m_height; } + int GetDepth() const { return m_depth; } + + void *GetRawAccess() const; + void *BeginRawAccess(); + void EndRawAccess(); + + bool HasAlpha() const { return m_hasAlpha; } + void UseAlpha( bool useAlpha ); + +public: +#if wxUSE_PALETTE + wxPalette m_bitmapPalette; +#endif // wxUSE_PALETTE + + wxMask * m_bitmapMask; // Optional mask + CGImageRef CreateCGImage() const; + + // returns true if the bitmap has a size that + // can be natively transferred into a true icon + // if no is returned GetIconRef will still produce + // an icon but it will be generated via a PICT and + // rescaled to 16 x 16 + bool HasNativeSize(); + + // caller should increase ref count if needed longer + // than the bitmap exists + IconRef GetIconRef(); + +#ifndef __WXOSX_IPHONE__ + // returns a Pict from the bitmap content + PicHandle GetPictHandle(); +#endif + + CGContextRef GetBitmapContext() const; + + int GetBytesPerRow() const { return m_bytesPerRow; } + private : + bool Create(int width , int height , int depth); + void Init(); + + int m_width; + int m_height; + int m_bytesPerRow; + int m_depth; + bool m_hasAlpha; + wxMemoryBuffer m_memBuf; + int m_rawAccessCount; + bool m_ok; + mutable CGImageRef m_cgImageRef; + + IconRef m_iconRef; +#ifndef __WXOSX_IPHONE__ + PicHandle m_pictHandle; +#endif + CGContextRef m_hBitmap; +}; + + +#define wxMAC_USE_PREMULTIPLIED_ALPHA 1 +static const int kBestByteAlignement = 16; +static const int kMaskBytesPerPixel = 1; + +static int GetBestBytesPerRow( int rawBytes ) +{ + return (((rawBytes)+kBestByteAlignement-1) & ~(kBestByteAlignement-1) ); +} + +#if wxUSE_GUI && !defined(__WXOSX_IPHONE__) + +// this is used for more controls than just the wxBitmap button, also for notebooks etc + +void wxMacCreateBitmapButton( ControlButtonContentInfo*info , const wxBitmap& bitmap , int forceType ) +{ + memset( info , 0 , sizeof(ControlButtonContentInfo) ) ; + if ( bitmap.Ok() ) + { + wxBitmapRefData * bmap = bitmap.GetBitmapData() ; + if ( bmap == NULL ) + return ; + + if ( forceType == 0 ) + { + forceType = kControlContentCGImageRef; + } + + if ( forceType == kControlContentIconRef ) + { + wxBitmap scaleBmp ; + wxBitmapRefData* bmp = bmap ; + + if ( !bmap->HasNativeSize() ) + { + // as PICT conversion will only result in a 16x16 icon, let's attempt + // a few scales for better results + + int w = bitmap.GetWidth() ; + int h = bitmap.GetHeight() ; + int sz = wxMax( w , h ) ; + if ( sz == 24 || sz == 64 ) + { + scaleBmp = wxBitmap( bitmap.ConvertToImage().Scale( w * 2 , h * 2 ) ) ; + bmp = scaleBmp.GetBitmapData() ; + } + } + + info->contentType = kControlContentIconRef ; + info->u.iconRef = bmp->GetIconRef() ; + AcquireIconRef( info->u.iconRef ) ; + } + else if ( forceType == kControlContentCGImageRef ) + { + info->contentType = kControlContentCGImageRef ; + info->u.imageRef = (CGImageRef) bmap->CreateCGImage() ; + } + else + { +#ifndef __LP64__ + info->contentType = kControlContentPictHandle ; + info->u.picture = bmap->GetPictHandle() ; +#endif + } + } +} + +CGImageRef wxMacCreateCGImageFromBitmap( const wxBitmap& bitmap ) +{ + wxBitmapRefData * bmap = bitmap.GetBitmapData() ; + if ( bmap == NULL ) + return NULL ; + return (CGImageRef) bmap->CreateCGImage(); +} + +void wxMacReleaseBitmapButton( ControlButtonContentInfo*info ) +{ + if ( info->contentType == kControlContentIconRef ) + { + ReleaseIconRef( info->u.iconRef ) ; + } + else if ( info->contentType == kControlNoContent ) + { + // there's no bitmap at all, fall through silently + } + else if ( info->contentType == kControlContentPictHandle ) + { + // owned by the bitmap, no release here + } + else if ( info->contentType == kControlContentCGImageRef ) + { + CGImageRelease( info->u.imageRef ) ; + } + else + { + wxFAIL_MSG(wxT("Unexpected bitmap type") ) ; + } +} + +#endif //wxUSE_BMPBUTTON + +#define M_BITMAPDATA ((wxBitmapRefData *)m_refData) + +void wxBitmapRefData::Init() +{ + m_width = 0 ; + m_height = 0 ; + m_depth = 0 ; + m_bytesPerRow = 0; + m_ok = false ; + m_bitmapMask = NULL ; + m_cgImageRef = NULL ; + +#ifndef __WXOSX_IPHONE__ + m_iconRef = NULL ; + m_pictHandle = NULL ; +#endif + m_hBitmap = NULL ; + + m_rawAccessCount = 0 ; + m_hasAlpha = false; +} + +wxBitmapRefData::wxBitmapRefData(const wxBitmapRefData &tocopy) +{ + Init(); + Create(tocopy.m_width, tocopy.m_height, tocopy.m_depth); + + if (tocopy.m_bitmapMask) + m_bitmapMask = new wxMask(*tocopy.m_bitmapMask); + else if (tocopy.m_hasAlpha) + UseAlpha(true); + + unsigned char* dest = (unsigned char*)GetRawAccess(); + unsigned char* source = (unsigned char*)tocopy.GetRawAccess(); + size_t numbytes = m_bytesPerRow * m_height; + memcpy( dest, source, numbytes ); +} + +wxBitmapRefData::wxBitmapRefData() +{ + Init() ; +} + +wxBitmapRefData::wxBitmapRefData( int w , int h , int d ) +{ + Init() ; + Create( w , h , d ) ; +} + +bool wxBitmapRefData::Create( int w , int h , int d ) +{ + m_width = wxMax(1, w); + m_height = wxMax(1, h); + m_depth = d ; + m_hBitmap = NULL ; + + m_bytesPerRow = GetBestBytesPerRow( w * 4 ) ; + size_t size = m_bytesPerRow * h ; + void* data = m_memBuf.GetWriteBuf( size ) ; + if ( data != NULL ) + { + memset( data , 0 , size ) ; + m_memBuf.UngetWriteBuf( size ) ; + + m_hBitmap = CGBitmapContextCreate((char*) data, m_width, m_height, 8, m_bytesPerRow, wxMacGetGenericRGBColorSpace(), kCGImageAlphaNoneSkipFirst ); + wxASSERT_MSG( m_hBitmap , wxT("Unable to create CGBitmapContext context") ) ; + CGContextTranslateCTM( m_hBitmap, 0, m_height ); + CGContextScaleCTM( m_hBitmap, 1, -1 ); + } /* data != NULL */ + m_ok = ( m_hBitmap != NULL ) ; + + return m_ok ; +} + +void wxBitmapRefData::UseAlpha( bool use ) +{ + if ( m_hasAlpha == use ) + return ; + + m_hasAlpha = use ; + + CGContextRelease( m_hBitmap ); + m_hBitmap = CGBitmapContextCreate((char*) m_memBuf.GetData(), m_width, m_height, 8, m_bytesPerRow, wxMacGetGenericRGBColorSpace(), m_hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst ); + wxASSERT_MSG( m_hBitmap , wxT("Unable to create CGBitmapContext context") ) ; + CGContextTranslateCTM( m_hBitmap, 0, m_height ); + CGContextScaleCTM( m_hBitmap, 1, -1 ); +} + +void *wxBitmapRefData::GetRawAccess() const +{ + wxCHECK_MSG( IsOk(), NULL , wxT("invalid bitmap") ) ; + return m_memBuf.GetData() ; +} + +void *wxBitmapRefData::BeginRawAccess() +{ + wxCHECK_MSG( IsOk(), NULL, wxT("invalid bitmap") ) ; + wxASSERT( m_rawAccessCount == 0 ) ; +#ifndef __WXOSX_IPHONE__ + wxASSERT_MSG( m_pictHandle == NULL && m_iconRef == NULL , + wxT("Currently, modifing bitmaps that are used in controls already is not supported") ) ; +#endif + ++m_rawAccessCount ; + + // we must destroy an existing cached image, as + // the bitmap data may change now + if ( m_cgImageRef ) + { + CGImageRelease( m_cgImageRef ) ; + m_cgImageRef = NULL ; + } + + return m_memBuf.GetData() ; +} + +void wxBitmapRefData::EndRawAccess() +{ + wxCHECK_RET( IsOk() , wxT("invalid bitmap") ) ; + wxASSERT( m_rawAccessCount == 1 ) ; + + --m_rawAccessCount ; +} + +bool wxBitmapRefData::HasNativeSize() +{ + int w = GetWidth() ; + int h = GetHeight() ; + int sz = wxMax( w , h ) ; + + return ( sz == 128 || sz == 48 || sz == 32 || sz == 16 ); +} + +#ifndef __WXOSX_IPHONE__ +IconRef wxBitmapRefData::GetIconRef() +{ + if ( m_iconRef == NULL ) + { + // Create Icon Family Handle + + IconFamilyHandle iconFamily = (IconFamilyHandle) NewHandle( 0 ); + + int w = GetWidth() ; + int h = GetHeight() ; + int sz = wxMax( w , h ) ; + + OSType dataType = 0 ; + OSType maskType = 0 ; + + switch (sz) + { + case 128: +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( UMAGetSystemVersion() >= 0x1050 ) + { + dataType = kIconServices128PixelDataARGB ; + } + else +#endif + { + dataType = kThumbnail32BitData ; + maskType = kThumbnail8BitMask ; + } + break; + + case 48: +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( UMAGetSystemVersion() >= 0x1050 ) + { + dataType = kIconServices48PixelDataARGB ; + } + else +#endif + { + dataType = kHuge32BitData ; + maskType = kHuge8BitMask ; + } + break; + + case 32: +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( UMAGetSystemVersion() >= 0x1050 ) + { + dataType = kIconServices32PixelDataARGB ; + } + else +#endif + { + dataType = kLarge32BitData ; + maskType = kLarge8BitMask ; + } + break; + + case 16: +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( UMAGetSystemVersion() >= 0x1050 ) + { + dataType = kIconServices16PixelDataARGB ; + } + else +#endif + { + dataType = kSmall32BitData ; + maskType = kSmall8BitMask ; + } + break; + + default: + break; + } + + if ( dataType != 0 ) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( maskType == 0 && UMAGetSystemVersion() >= 0x1050 ) + { + size_t datasize = sz * sz * 4 ; + Handle data = NewHandle( datasize ) ; + HLock( data ) ; + unsigned char* ptr = (unsigned char*) *data ; + memset( ptr, 0, datasize ); + bool hasAlpha = HasAlpha() ; + wxMask *mask = m_bitmapMask ; + unsigned char * sourcePtr = (unsigned char*) GetRawAccess() ; + unsigned char * masksourcePtr = mask ? (unsigned char*) mask->GetRawAccess() : NULL ; + + for ( int y = 0 ; y < h ; ++y, sourcePtr += m_bytesPerRow , masksourcePtr += mask ? mask->GetBytesPerRow() : 0 ) + { + unsigned char * source = sourcePtr; + unsigned char * masksource = masksourcePtr; + unsigned char * dest = ptr + y * sz * 4 ; + unsigned char a, r, g, b; + + for ( int x = 0 ; x < w ; ++x ) + { + a = *source ++ ; + r = *source ++ ; + g = *source ++ ; + b = *source ++ ; + + if ( mask ) + { + a = 0xFF - *masksource++ ; + } + else if ( !hasAlpha ) + a = 0xFF ; + else + { +#if wxMAC_USE_PREMULTIPLIED_ALPHA + // this must be non-premultiplied data + if ( a != 0xFF && a!= 0 ) + { + r = r * 255 / a; + g = g * 255 / a; + b = b * 255 / a; + } +#endif + } + *dest++ = a ; + *dest++ = r ; + *dest++ = g ; + *dest++ = b ; + + } + } + HUnlock( data ); + OSStatus err = SetIconFamilyData( iconFamily, dataType , data ); + wxASSERT_MSG( err == noErr , wxT("Error when adding bitmap") ); + DisposeHandle( data ); + } + else +#endif + { + // setup the header properly + + Handle data = NULL ; + Handle maskdata = NULL ; + unsigned char * maskptr = NULL ; + unsigned char * ptr = NULL ; + size_t datasize, masksize ; + + datasize = sz * sz * 4 ; + data = NewHandle( datasize ) ; + HLock( data ) ; + ptr = (unsigned char*) *data ; + memset( ptr , 0, datasize ) ; + + masksize = sz * sz ; + maskdata = NewHandle( masksize ) ; + HLock( maskdata ) ; + maskptr = (unsigned char*) *maskdata ; + memset( maskptr , 0 , masksize ) ; + + bool hasAlpha = HasAlpha() ; + wxMask *mask = m_bitmapMask ; + unsigned char * sourcePtr = (unsigned char*) GetRawAccess() ; + unsigned char * masksourcePtr = mask ? (unsigned char*) mask->GetRawAccess() : NULL ; + + for ( int y = 0 ; y < h ; ++y, sourcePtr += m_bytesPerRow , masksourcePtr += mask ? mask->GetBytesPerRow() : 0 ) + { + unsigned char * source = sourcePtr; + unsigned char * masksource = masksourcePtr; + unsigned char * dest = ptr + y * sz * 4 ; + unsigned char * maskdest = maskptr + y * sz ; + unsigned char a, r, g, b; + + for ( int x = 0 ; x < w ; ++x ) + { + a = *source ++ ; + r = *source ++ ; + g = *source ++ ; + b = *source ++ ; + + *dest++ = 0 ; + *dest++ = r ; + *dest++ = g ; + *dest++ = b ; + + if ( mask ) + *maskdest++ = 0xFF - *masksource++ ; + else if ( hasAlpha ) + *maskdest++ = a ; + else + *maskdest++ = 0xFF ; + } + } + + OSStatus err = SetIconFamilyData( iconFamily, dataType , data ) ; + wxASSERT_MSG( err == noErr , wxT("Error when adding bitmap") ) ; + + err = SetIconFamilyData( iconFamily, maskType , maskdata ) ; + wxASSERT_MSG( err == noErr , wxT("Error when adding mask") ) ; + + HUnlock( data ) ; + HUnlock( maskdata ) ; + DisposeHandle( data ) ; + DisposeHandle( maskdata ) ; + } + } + else + { + PicHandle pic = GetPictHandle() ; + SetIconFamilyData( iconFamily, 'PICT' , (Handle) pic ) ; + } + // transform into IconRef + + // cleaner version existing from 10.3 upwards + HLock((Handle) iconFamily); + OSStatus err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &m_iconRef ); + HUnlock((Handle) iconFamily); + DisposeHandle( (Handle) iconFamily ) ; + + wxCHECK_MSG( err == noErr, NULL, wxT("Error when constructing icon ref") ); + } + + return m_iconRef ; +} + +PicHandle wxBitmapRefData::GetPictHandle() +{ + if ( m_pictHandle == NULL ) + { +#ifndef __LP64__ + GraphicsExportComponent exporter = 0; + OSStatus err = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePicture, &exporter); + if (noErr == err) + { + m_pictHandle = (PicHandle) NewHandle(0); + if ( m_pictHandle ) + { + // QT does not correctly export the mask + // TODO if we get around to it create a synthetic PICT with the CopyBits and Mask commands + CGImageRef imageRef = CreateCGImage(); + err = GraphicsExportSetInputCGImage( exporter, imageRef ); + err = GraphicsExportSetOutputHandle(exporter, (Handle)m_pictHandle); + err = GraphicsExportDoExport(exporter, NULL); + CGImageRelease( imageRef ); + + size_t handleSize = GetHandleSize( (Handle) m_pictHandle ); + // the 512 bytes header is only needed for pict files, but not in memory + if ( handleSize >= 512 ) + { + memmove( *m_pictHandle , (char*)(*m_pictHandle)+512, handleSize - 512 ); + SetHandleSize( (Handle) m_pictHandle, handleSize - 512 ); + } + } + CloseComponent( exporter ); + } +#endif + } + + return m_pictHandle ; +} +#endif + +CGImageRef wxBitmapRefData::CreateCGImage() const +{ + wxASSERT( m_ok ) ; + wxASSERT( m_rawAccessCount >= 0 ) ; + CGImageRef image ; + if ( m_rawAccessCount > 0 || m_cgImageRef == NULL ) + { + if ( m_depth != 1 && m_bitmapMask == NULL ) + { + if ( m_bitmapMask ) + { + CGImageRef tempImage = CGBitmapContextCreateImage( m_hBitmap ); + CGImageRef tempMask = CGBitmapContextCreateImage((CGContextRef) m_bitmapMask->GetHBITMAP() ); + image = CGImageCreateWithMask( tempImage, tempMask ); + CGImageRelease(tempMask); + CGImageRelease(tempImage); + } + else + image = CGBitmapContextCreateImage( m_hBitmap ); + } + else + { + size_t imageSize = m_height * m_bytesPerRow ; + void * dataBuffer = m_memBuf.GetData() ; + int w = m_width ; + int h = m_height ; + CGImageAlphaInfo alphaInfo = kCGImageAlphaNoneSkipFirst ; + wxMemoryBuffer membuf; + + if ( m_bitmapMask ) + { + alphaInfo = kCGImageAlphaFirst ; + unsigned char *destalphastart = (unsigned char*) membuf.GetWriteBuf( imageSize ) ; + memcpy( destalphastart , dataBuffer , imageSize ) ; + unsigned char *sourcemaskstart = (unsigned char *) m_bitmapMask->GetRawAccess() ; + int maskrowbytes = m_bitmapMask->GetBytesPerRow() ; + for ( int y = 0 ; y < h ; ++y , destalphastart += m_bytesPerRow, sourcemaskstart += maskrowbytes) + { + unsigned char *sourcemask = sourcemaskstart ; + unsigned char *destalpha = destalphastart ; + for ( int x = 0 ; x < w ; ++x , sourcemask += kMaskBytesPerPixel , destalpha += 4 ) + { + *destalpha = 0xFF - *sourcemask ; + } + } + membuf.UngetWriteBuf( imageSize ); + } + else + { + if ( m_hasAlpha ) + { +#if wxMAC_USE_PREMULTIPLIED_ALPHA + alphaInfo = kCGImageAlphaPremultipliedFirst ; +#else + alphaInfo = kCGImageAlphaFirst ; +#endif + } + + membuf = m_memBuf; + } + + CGDataProviderRef dataProvider = NULL ; + if ( m_depth == 1 ) + { + // TODO CHECK ALIGNMENT + wxMemoryBuffer maskBuf; + unsigned char * maskBufData = (unsigned char*) maskBuf.GetWriteBuf( m_width * m_height ); + unsigned char * bufData = (unsigned char *) membuf.GetData() ; + // copy one color component + size_t i = 0; + for( int y = 0 ; y < m_height ; bufData+= m_bytesPerRow, ++y ) + { + unsigned char *bufDataIter = bufData+3; + for ( int x = 0 ; x < m_width ; bufDataIter += 4, ++x, ++i ) + { + maskBufData[i] = *bufDataIter; + } + } + maskBuf.UngetWriteBuf( m_width * m_height ); + + dataProvider = + wxMacCGDataProviderCreateWithMemoryBuffer( maskBuf ); + + image = ::CGImageMaskCreate( w, h, 8, 8, m_width , dataProvider, NULL, false ); + } + else + { + CGColorSpaceRef colorSpace = wxMacGetGenericRGBColorSpace(); + dataProvider = wxMacCGDataProviderCreateWithMemoryBuffer( membuf ); + image = + ::CGImageCreate( + w, h, 8 , 32 , m_bytesPerRow , colorSpace, alphaInfo , + dataProvider, NULL , false , kCGRenderingIntentDefault ); + } + CGDataProviderRelease( dataProvider); + } + } + else + { + image = m_cgImageRef ; + CGImageRetain( image ) ; + } + + if ( m_rawAccessCount == 0 && m_cgImageRef == NULL) + { + // we keep it for later use + m_cgImageRef = image ; + CGImageRetain( image ) ; + } + + return image ; +} + +CGContextRef wxBitmapRefData::GetBitmapContext() const +{ + return m_hBitmap; +} + +void wxBitmapRefData::Free() +{ + wxASSERT_MSG( m_rawAccessCount == 0 , wxT("Bitmap still selected when destroyed") ) ; + + if ( m_cgImageRef ) + { + CGImageRelease( m_cgImageRef ) ; + m_cgImageRef = NULL ; + } +#ifndef __WXOSX_IPHONE__ + if ( m_iconRef ) + { + ReleaseIconRef( m_iconRef ) ; + m_iconRef = NULL ; + } + +#ifndef __LP64__ + if ( m_pictHandle ) + { + KillPicture( m_pictHandle ) ; + m_pictHandle = NULL ; + } +#endif +#endif + if ( m_hBitmap ) + { + CGContextRelease(m_hBitmap); + m_hBitmap = NULL ; + } + + if (m_bitmapMask) + { + delete m_bitmapMask; + m_bitmapMask = NULL; + } +} + +wxBitmapRefData::~wxBitmapRefData() +{ + Free() ; +} + +bool wxBitmap::CopyFromIcon(const wxIcon& icon) +{ + bool created = false ; + int w = icon.GetWidth() ; + int h = icon.GetHeight() ; + + Create( icon.GetWidth() , icon.GetHeight() ) ; +#ifndef __WXOSX_IPHONE__ + if ( w == h && ( w == 16 || w == 32 || w == 48 || w == 128 ) ) + { + IconFamilyHandle iconFamily = NULL ; + Handle imagehandle = NewHandle( 0 ) ; + Handle maskhandle = NewHandle( 0 ) ; + + OSType maskType = 0; + OSType dataType = 0; + IconSelectorValue selector = 0 ; + + switch (w) + { + case 128: + dataType = kThumbnail32BitData ; + maskType = kThumbnail8BitMask ; + selector = kSelectorAllAvailableData ; + break; + + case 48: + dataType = kHuge32BitData ; + maskType = kHuge8BitMask ; + selector = kSelectorHuge32Bit | kSelectorHuge8BitMask ; + break; + + case 32: + dataType = kLarge32BitData ; + maskType = kLarge8BitMask ; + selector = kSelectorLarge32Bit | kSelectorLarge8BitMask ; + break; + + case 16: + dataType = kSmall32BitData ; + maskType = kSmall8BitMask ; + selector = kSelectorSmall32Bit | kSelectorSmall8BitMask ; + break; + + default: + break; + } + + OSStatus err = IconRefToIconFamily( MAC_WXHICON(icon.GetHICON()) , selector , &iconFamily ) ; + + err = GetIconFamilyData( iconFamily , dataType , imagehandle ) ; + err = GetIconFamilyData( iconFamily , maskType , maskhandle ) ; + size_t imagehandlesize = GetHandleSize( imagehandle ) ; + size_t maskhandlesize = GetHandleSize( maskhandle ) ; + + if ( imagehandlesize != 0 && maskhandlesize != 0 ) + { + wxASSERT( GetHandleSize( imagehandle ) == w * 4 * h ) ; + wxASSERT( GetHandleSize( maskhandle ) == w * h ) ; + + UseAlpha() ; + + unsigned char *source = (unsigned char *) *imagehandle ; + unsigned char *sourcemask = (unsigned char *) *maskhandle ; + unsigned char* destination = (unsigned char*) BeginRawAccess() ; + + for ( int y = 0 ; y < h ; ++y ) + { + for ( int x = 0 ; x < w ; ++x ) + { + unsigned char a = *sourcemask++; + *destination++ = a; + source++ ; +#if wxMAC_USE_PREMULTIPLIED_ALPHA + *destination++ = ( (*source++) * a + 127 ) / 255; + *destination++ = ( (*source++) * a + 127 ) / 255; + *destination++ = ( (*source++) * a + 127 ) / 255; +#else + *destination++ = *source++ ; + *destination++ = *source++ ; + *destination++ = *source++ ; +#endif + } + } + + EndRawAccess() ; + DisposeHandle( imagehandle ) ; + DisposeHandle( maskhandle ) ; + created = true ; + } + + DisposeHandle( (Handle) iconFamily ) ; + } +#endif + if ( !created ) + { + wxMemoryDC dc ; + dc.SelectObject( *this ) ; + dc.DrawIcon( icon , 0 , 0 ) ; + dc.SelectObject( wxNullBitmap ) ; + } + + return true; +} + +wxBitmap::wxBitmap() +{ +} + +wxBitmap::~wxBitmap() +{ +} + +wxBitmap::wxBitmap(const char bits[], int the_width, int the_height, int no_bits) +{ + wxBitmapRefData* bitmapRefData; + + m_refData = bitmapRefData = new wxBitmapRefData( the_width , the_height , no_bits ) ; + + if (bitmapRefData->IsOk()) + { + if ( no_bits == 1 ) + { + int linesize = ( the_width / (sizeof(unsigned char) * 8)) ; + if ( the_width % (sizeof(unsigned char) * 8) ) + linesize += sizeof(unsigned char); + + unsigned char* linestart = (unsigned char*) bits ; + unsigned char* destptr = (unsigned char*) BeginRawAccess() ; + + for ( int y = 0 ; y < the_height ; ++y , linestart += linesize, destptr += M_BITMAPDATA->GetBytesPerRow() ) + { + unsigned char* destination = destptr; + int index, bit, mask; + + for ( int x = 0 ; x < the_width ; ++x ) + { + index = x / 8 ; + bit = x % 8 ; + mask = 1 << bit ; + + if ( linestart[index] & mask ) + { + *destination++ = 0xFF ; + *destination++ = 0 ; + *destination++ = 0 ; + *destination++ = 0 ; + } + else + { + *destination++ = 0xFF ; + *destination++ = 0xFF ; + *destination++ = 0xFF ; + *destination++ = 0xFF ; + } + } + } + + EndRawAccess() ; + } + else + { + wxFAIL_MSG(wxT("multicolor BITMAPs not yet implemented")); + } + } /* bitmapRefData->IsOk() */ +} + +wxBitmap::wxBitmap(int w, int h, int d) +{ + (void)Create(w, h, d); +} + +wxBitmap::wxBitmap(const void* data, wxBitmapType type, int width, int height, int depth) +{ + (void) Create(data, type, width, height, depth); +} + +wxBitmap::wxBitmap(const wxString& filename, wxBitmapType type) +{ + LoadFile(filename, type); +} + +wxGDIRefData* wxBitmap::CreateGDIRefData() const +{ + return new wxBitmapRefData; +} + +wxGDIRefData* wxBitmap::CloneGDIRefData(const wxGDIRefData* data) const +{ + return new wxBitmapRefData(*wx_static_cast(const wxBitmapRefData *, data)); +} + +void * wxBitmap::GetRawAccess() const +{ + wxCHECK_MSG( Ok() , NULL , wxT("invalid bitmap") ) ; + + return M_BITMAPDATA->GetRawAccess() ; +} + +void * wxBitmap::BeginRawAccess() +{ + wxCHECK_MSG( Ok() , NULL , wxT("invalid bitmap") ) ; + + return M_BITMAPDATA->BeginRawAccess() ; +} + +void wxBitmap::EndRawAccess() +{ + wxCHECK_RET( Ok() , wxT("invalid bitmap") ) ; + + M_BITMAPDATA->EndRawAccess() ; +} + +CGImageRef wxBitmap::CreateCGImage() const +{ + wxCHECK_MSG( Ok(), NULL , wxT("invalid bitmap") ) ; + + return M_BITMAPDATA->CreateCGImage() ; +} + +#ifndef __WXOSX_IPHONE__ +IconRef wxBitmap::GetIconRef() const +{ + wxCHECK_MSG( Ok(), NULL , wxT("invalid bitmap") ) ; + + return M_BITMAPDATA->GetIconRef() ; +} + +IconRef wxBitmap::CreateIconRef() const +{ + IconRef icon = GetIconRef(); + verify_noerr( AcquireIconRef(icon) ); + return icon; +} +#endif + +wxBitmap wxBitmap::GetSubBitmap(const wxRect &rect) const +{ + wxCHECK_MSG( Ok() && + (rect.x >= 0) && (rect.y >= 0) && + (rect.x+rect.width <= GetWidth()) && + (rect.y+rect.height <= GetHeight()), + wxNullBitmap, wxT("invalid bitmap or bitmap region") ); + + wxBitmap ret( rect.width, rect.height, GetDepth() ); + wxASSERT_MSG( ret.Ok(), wxT("GetSubBitmap error") ); + + int destwidth = rect.width ; + int destheight = rect.height ; + + { + unsigned char *sourcedata = (unsigned char*) GetRawAccess() ; + unsigned char *destdata = (unsigned char*) ret.BeginRawAccess() ; + wxASSERT( (sourcedata != NULL) && (destdata != NULL) ) ; + + int sourcelinesize = GetBitmapData()->GetBytesPerRow() ; + int destlinesize = ret.GetBitmapData()->GetBytesPerRow() ; + unsigned char *source = sourcedata + rect.x * 4 + rect.y * sourcelinesize ; + unsigned char *dest = destdata ; + + for (int yy = 0; yy < destheight; ++yy, source += sourcelinesize , dest += destlinesize) + { + memcpy( dest , source , destlinesize ) ; + } + } + + ret.EndRawAccess() ; + + if ( M_BITMAPDATA->m_bitmapMask ) + { + wxMemoryBuffer maskbuf ; + int rowBytes = GetBestBytesPerRow( destwidth * kMaskBytesPerPixel ); + size_t maskbufsize = rowBytes * destheight ; + + int sourcelinesize = M_BITMAPDATA->m_bitmapMask->GetBytesPerRow() ; + int destlinesize = rowBytes ; + + unsigned char *source = (unsigned char *) M_BITMAPDATA->m_bitmapMask->GetRawAccess() ; + unsigned char *destdata = (unsigned char * ) maskbuf.GetWriteBuf( maskbufsize ) ; + wxASSERT( (source != NULL) && (destdata != NULL) ) ; + + source += rect.x * kMaskBytesPerPixel + rect.y * sourcelinesize ; + unsigned char *dest = destdata ; + + for (int yy = 0; yy < destheight; ++yy, source += sourcelinesize , dest += destlinesize) + { + memcpy( dest , source , destlinesize ) ; + } + + maskbuf.UngetWriteBuf( maskbufsize ) ; + ret.SetMask( new wxMask( maskbuf , destwidth , destheight , rowBytes ) ) ; + } + else if ( HasAlpha() ) + ret.UseAlpha() ; + + return ret; +} + +bool wxBitmap::Create(int w, int h, int d) +{ + UnRef(); + + if ( d < 0 ) + d = wxDisplayDepth() ; + + m_refData = new wxBitmapRefData( w , h , d ); + + return M_BITMAPDATA->IsOk() ; +} + +bool wxBitmap::LoadFile(const wxString& filename, wxBitmapType type) +{ + UnRef(); + + wxBitmapHandler *handler = FindHandler(type); + + if ( handler ) + { + m_refData = new wxBitmapRefData; + + return handler->LoadFile(this, filename, type, -1, -1); + } + else + { +#if wxUSE_IMAGE + wxImage loadimage(filename, type); + if (loadimage.Ok()) + { + *this = loadimage; + + return true; + } +#endif + } + + wxLogWarning(wxT("no bitmap handler for type %d defined."), type); + + return false; +} + +bool wxBitmap::Create(const void* data, wxBitmapType type, int width, int height, int depth) +{ + UnRef(); + + m_refData = new wxBitmapRefData; + + wxBitmapHandler *handler = FindHandler(type); + + if ( handler == NULL ) + { + wxLogWarning(wxT("no bitmap handler for type %d defined."), type); + + return false; + } + + return handler->Create(this, data, type, width, height, depth); +} + +#if wxUSE_IMAGE + +wxBitmap::wxBitmap(const wxImage& image, int depth) +{ + wxCHECK_RET( image.Ok(), wxT("invalid image") ); + + // width and height of the device-dependent bitmap + int width = image.GetWidth(); + int height = image.GetHeight(); + + wxBitmapRefData* bitmapRefData; + + m_refData = bitmapRefData = new wxBitmapRefData( width , height , depth ) ; + + if ( bitmapRefData->IsOk()) + { + // Create picture + + bool hasAlpha = false ; + + if ( image.HasMask() ) + { + // takes precedence, don't mix with alpha info + } + else + { + hasAlpha = image.HasAlpha() ; + } + + if ( hasAlpha ) + UseAlpha() ; + + unsigned char* destinationstart = (unsigned char*) BeginRawAccess() ; + register unsigned char* data = image.GetData(); + if ( destinationstart != NULL && data != NULL ) + { + const unsigned char *alpha = hasAlpha ? image.GetAlpha() : NULL ; + for (int y = 0; y < height; destinationstart += M_BITMAPDATA->GetBytesPerRow(), y++) + { + unsigned char * destination = destinationstart; + for (int x = 0; x < width; x++) + { + if ( hasAlpha ) + { + const unsigned char a = *alpha++; + *destination++ = a ; + + #if wxMAC_USE_PREMULTIPLIED_ALPHA + *destination++ = ((*data++) * a + 127) / 255 ; + *destination++ = ((*data++) * a + 127) / 255 ; + *destination++ = ((*data++) * a + 127) / 255 ; + #else + *destination++ = *data++ ; + *destination++ = *data++ ; + *destination++ = *data++ ; + #endif + } + else + { + *destination++ = 0xFF ; + *destination++ = *data++ ; + *destination++ = *data++ ; + *destination++ = *data++ ; + } + } + } + + EndRawAccess() ; + } + if ( image.HasMask() ) + SetMask( new wxMask( *this , wxColour( image.GetMaskRed() , image.GetMaskGreen() , image.GetMaskBlue() ) ) ) ; + } /* bitmapRefData->IsOk() */ +} + +wxImage wxBitmap::ConvertToImage() const +{ + wxImage image; + + wxCHECK_MSG( Ok(), wxNullImage, wxT("invalid bitmap") ); + + // create an wxImage object + int width = GetWidth(); + int height = GetHeight(); + image.Create( width, height ); + + unsigned char *data = image.GetData(); + wxCHECK_MSG( data, wxNullImage, wxT("Could not allocate data for image") ); + + unsigned char* sourcestart = (unsigned char*) GetRawAccess() ; + + bool hasAlpha = false ; + bool hasMask = false ; + int maskBytesPerRow = 0 ; + unsigned char *alpha = NULL ; + unsigned char *mask = NULL ; + + if ( HasAlpha() ) + hasAlpha = true ; + + if ( GetMask() ) + { + hasMask = true ; + mask = (unsigned char*) GetMask()->GetRawAccess() ; + maskBytesPerRow = GetMask()->GetBytesPerRow() ; + } + + if ( hasAlpha ) + { + image.SetAlpha() ; + alpha = image.GetAlpha() ; + } + + int index = 0; + + // The following masking algorithm is the same as well in msw/gtk: + // the colour used as transparent one in wxImage and the one it is + // replaced with when it actually occurs in the bitmap + static const int MASK_RED = 1; + static const int MASK_GREEN = 2; + static const int MASK_BLUE = 3; + static const int MASK_BLUE_REPLACEMENT = 2; + + for (int yy = 0; yy < height; yy++ , sourcestart += M_BITMAPDATA->GetBytesPerRow() , mask += maskBytesPerRow ) + { + unsigned char * maskp = mask ; + unsigned char * source = sourcestart; + unsigned char a, r, g, b; + long color; + + for (int xx = 0; xx < width; xx++) + { + color = *((long*) source) ; +#ifdef WORDS_BIGENDIAN + a = ((color&0xFF000000) >> 24) ; + r = ((color&0x00FF0000) >> 16) ; + g = ((color&0x0000FF00) >> 8) ; + b = (color&0x000000FF); +#else + b = ((color&0xFF000000) >> 24) ; + g = ((color&0x00FF0000) >> 16) ; + r = ((color&0x0000FF00) >> 8) ; + a = (color&0x000000FF); +#endif + if ( hasMask ) + { + if ( *maskp++ == 0xFF ) + { + r = MASK_RED ; + g = MASK_GREEN ; + b = MASK_BLUE ; + } + else if ( r == MASK_RED && g == MASK_GREEN && b == MASK_BLUE ) + b = MASK_BLUE_REPLACEMENT ; + } + else if ( hasAlpha ) + { + *alpha++ = a ; +#if wxMAC_USE_PREMULTIPLIED_ALPHA + // this must be non-premultiplied data + if ( a != 0xFF && a!= 0 ) + { + r = r * 255 / a; + g = g * 255 / a; + b = b * 255 / a; + } +#endif + } + + data[index ] = r ; + data[index + 1] = g ; + data[index + 2] = b ; + + index += 3; + source += 4 ; + } + } + + if ( hasMask ) + image.SetMaskColour( MASK_RED, MASK_GREEN, MASK_BLUE ); + + return image; +} + +#endif //wxUSE_IMAGE + +bool wxBitmap::SaveFile( const wxString& filename, + wxBitmapType type, const wxPalette *palette ) const +{ + bool success = false; + wxBitmapHandler *handler = FindHandler(type); + + if ( handler ) + { + success = handler->SaveFile(this, filename, type, palette); + } + else + { +#if wxUSE_IMAGE + wxImage image = ConvertToImage(); + success = image.SaveFile(filename, type); +#else + wxLogWarning(wxT("no bitmap handler for type %d defined."), type); +#endif + } + + return success; +} + +int wxBitmap::GetHeight() const +{ + wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") ); + + return M_BITMAPDATA->GetHeight(); +} + +int wxBitmap::GetWidth() const +{ + wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") ); + + return M_BITMAPDATA->GetWidth() ; +} + +int wxBitmap::GetDepth() const +{ + wxCHECK_MSG( Ok(), -1, wxT("invalid bitmap") ); + + return M_BITMAPDATA->GetDepth(); +} + +wxMask *wxBitmap::GetMask() const +{ + wxCHECK_MSG( Ok(), (wxMask *) NULL, wxT("invalid bitmap") ); + + return M_BITMAPDATA->m_bitmapMask; +} + +bool wxBitmap::HasAlpha() const +{ + wxCHECK_MSG( Ok(), false , wxT("invalid bitmap") ); + + return M_BITMAPDATA->HasAlpha() ; +} + +void wxBitmap::SetWidth(int w) +{ + AllocExclusive(); + M_BITMAPDATA->SetWidth(w); +} + +void wxBitmap::SetHeight(int h) +{ + AllocExclusive(); + M_BITMAPDATA->SetHeight(h); +} + +void wxBitmap::SetDepth(int d) +{ + AllocExclusive(); + M_BITMAPDATA->SetDepth(d); +} + +void wxBitmap::SetOk(bool isOk) +{ + AllocExclusive(); + M_BITMAPDATA->SetOk(isOk); +} + +#if wxUSE_PALETTE +wxPalette *wxBitmap::GetPalette() const +{ + wxCHECK_MSG( Ok(), NULL, wxT("Invalid bitmap GetPalette()") ); + + return &M_BITMAPDATA->m_bitmapPalette; +} + +void wxBitmap::SetPalette(const wxPalette& palette) +{ + AllocExclusive(); + M_BITMAPDATA->m_bitmapPalette = palette ; +} +#endif // wxUSE_PALETTE + +void wxBitmap::SetMask(wxMask *mask) +{ + AllocExclusive(); + // Remove existing mask if there is one. + delete M_BITMAPDATA->m_bitmapMask; + + M_BITMAPDATA->m_bitmapMask = mask ; +} + +WXHBITMAP wxBitmap::GetHBITMAP(WXHBITMAP* mask) const +{ + wxUnusedVar(mask); + + return WXHBITMAP(M_BITMAPDATA->GetBitmapContext()); +} + +// ---------------------------------------------------------------------------- +// wxMask +// ---------------------------------------------------------------------------- + +wxMask::wxMask() +{ + Init() ; +} + +wxMask::wxMask(const wxMask &tocopy) +{ + Init(); + + m_bytesPerRow = tocopy.m_bytesPerRow; + m_width = tocopy.m_width; + m_height = tocopy.m_height; + + size_t size = m_bytesPerRow * m_height; + unsigned char* dest = (unsigned char*)m_memBuf.GetWriteBuf( size ); + unsigned char* source = (unsigned char*)tocopy.m_memBuf.GetData(); + memcpy( dest, source, size ); + m_memBuf.UngetWriteBuf( size ) ; + RealizeNative() ; +} + +// Construct a mask from a bitmap and a colour indicating +// the transparent area +wxMask::wxMask( const wxBitmap& bitmap, const wxColour& colour ) +{ + Init() ; + Create( bitmap, colour ); +} + +// Construct a mask from a mono bitmap (copies the bitmap). +wxMask::wxMask( const wxBitmap& bitmap ) +{ + Init() ; + Create( bitmap ); +} + +// Construct a mask from a mono bitmap (copies the bitmap). + +wxMask::wxMask( const wxMemoryBuffer& data, int width , int height , int bytesPerRow ) +{ + Init() ; + Create( data, width , height , bytesPerRow ); +} + +wxMask::~wxMask() +{ + if ( m_maskBitmap ) + { + CGContextRelease( (CGContextRef) m_maskBitmap ); + m_maskBitmap = NULL ; + } +} + +void wxMask::Init() +{ + m_width = m_height = m_bytesPerRow = 0 ; + m_maskBitmap = NULL ; +} + +void *wxMask::GetRawAccess() const +{ + return m_memBuf.GetData() ; +} + +// The default ColorTable for k8IndexedGrayPixelFormat in Intel appears to be broken, so we'll use an non-indexed +// bitmap mask instead; in order to keep the code simple, the change applies to PowerPC implementations as well + +void wxMask::RealizeNative() +{ + if ( m_maskBitmap ) + { + CGContextRelease( (CGContextRef) m_maskBitmap ); + m_maskBitmap = NULL ; + } + + CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray(); + // from MouseTracking sample : + // Ironically, due to a bug in CGImageCreateWithMask, you cannot use + // CGColorSpaceCreateWithName(kCGColorSpaceGenericGray) at this point! + + m_maskBitmap = CGBitmapContextCreate((char*) m_memBuf.GetData(), m_width, m_height, 8, m_bytesPerRow, colorspace, + kCGImageAlphaNone ); + CGColorSpaceRelease( colorspace ); + wxASSERT_MSG( m_maskBitmap , wxT("Unable to create CGBitmapContext context") ) ; +} + +// Create a mask from a mono bitmap (copies the bitmap). + +bool wxMask::Create(const wxMemoryBuffer& data,int width , int height , int bytesPerRow) +{ + m_memBuf = data ; + m_width = width ; + m_height = height ; + m_bytesPerRow = bytesPerRow ; + + wxASSERT( data.GetDataLen() == (size_t)(height * bytesPerRow) ) ; + + RealizeNative() ; + + return true ; +} + +// Create a mask from a mono bitmap (copies the bitmap). +bool wxMask::Create(const wxBitmap& bitmap) +{ + m_width = bitmap.GetWidth() ; + m_height = bitmap.GetHeight() ; + m_bytesPerRow = GetBestBytesPerRow( m_width * kMaskBytesPerPixel ) ; + + size_t size = m_bytesPerRow * m_height ; + unsigned char * destdatabase = (unsigned char*) m_memBuf.GetWriteBuf( size ) ; + wxASSERT( destdatabase != NULL ) ; + + memset( destdatabase , 0 , size ) ; + unsigned char * srcdata = (unsigned char*) bitmap.GetRawAccess() ; + + for ( int y = 0 ; y < m_height ; ++y , destdatabase += m_bytesPerRow ) + { + unsigned char *destdata = destdatabase ; + unsigned char r, g, b; + + for ( int x = 0 ; x < m_width ; ++x ) + { + srcdata++ ; + r = *srcdata++ ; + g = *srcdata++ ; + b = *srcdata++ ; + + if ( ( r + g + b ) > 0x10 ) + *destdata++ = 0xFF ; + else + *destdata++ = 0x00 ; + } + } + + m_memBuf.UngetWriteBuf( size ) ; + RealizeNative() ; + + return true; +} + +// Create a mask from a bitmap and a colour indicating +// the transparent area +bool wxMask::Create(const wxBitmap& bitmap, const wxColour& colour) +{ + m_width = bitmap.GetWidth() ; + m_height = bitmap.GetHeight() ; + m_bytesPerRow = GetBestBytesPerRow( m_width * kMaskBytesPerPixel ) ; + + size_t size = m_bytesPerRow * m_height ; + unsigned char * destdatabase = (unsigned char*) m_memBuf.GetWriteBuf( size ) ; + wxASSERT( destdatabase != NULL ) ; + + memset( destdatabase , 0 , size ) ; + unsigned char * srcdatabase = (unsigned char*) bitmap.GetRawAccess() ; + size_t sourceBytesRow = bitmap.GetBitmapData()->GetBytesPerRow(); + + for ( int y = 0 ; y < m_height ; ++y , srcdatabase+= sourceBytesRow, destdatabase += m_bytesPerRow) + { + unsigned char *srcdata = srcdatabase ; + unsigned char *destdata = destdatabase ; + unsigned char r, g, b; + + for ( int x = 0 ; x < m_width ; ++x ) + { + srcdata++ ; + r = *srcdata++ ; + g = *srcdata++ ; + b = *srcdata++ ; + + if ( colour == wxColour( r , g , b ) ) + *destdata++ = 0xFF ; + else + *destdata++ = 0x00 ; + } + } + + m_memBuf.UngetWriteBuf( size ) ; + RealizeNative() ; + + return true; +} + +WXHBITMAP wxMask::GetHBITMAP() const +{ + return m_maskBitmap ; +} + +// ---------------------------------------------------------------------------- +// Standard Handlers +// ---------------------------------------------------------------------------- + +#if !defined( __LP64__ ) && !defined(__WXOSX_IPHONE__) + +class WXDLLEXPORT wxPICTResourceHandler: public wxBitmapHandler +{ + DECLARE_DYNAMIC_CLASS(wxPICTResourceHandler) + +public: + inline wxPICTResourceHandler() + { + SetName(wxT("Macintosh Pict resource")); + SetExtension(wxEmptyString); + SetType(wxBITMAP_TYPE_PICT_RESOURCE); + }; + + virtual bool LoadFile(wxBitmap *bitmap, const wxString& name, long flags, + int desiredWidth, int desiredHeight); +}; + +IMPLEMENT_DYNAMIC_CLASS(wxPICTResourceHandler, wxBitmapHandler) + + +bool wxPICTResourceHandler::LoadFile(wxBitmap *bitmap, + const wxString& name, + long WXUNUSED(flags), + int WXUNUSED(desiredWidth), + int WXUNUSED(desiredHeight)) +{ +#if wxUSE_METAFILE + Str255 theName ; + wxMacStringToPascal( name , theName ) ; + + PicHandle thePict = (PicHandle ) GetNamedResource( 'PICT' , theName ) ; + if ( thePict ) + { + wxMetafile mf ; + + mf.SetPICT( thePict ) ; + bitmap->Create( mf.GetWidth() , mf.GetHeight() ) ; + wxMemoryDC dc ; + dc.SelectObject( *bitmap ) ; + mf.Play( &dc ) ; + dc.SelectObject( wxNullBitmap ) ; + + return true ; + } +#endif + + return false ; +} +#endif + +void wxBitmap::InitStandardHandlers() +{ +#if !defined( __LP64__ ) && !defined(__WXOSX_IPHONE__) + AddHandler( new wxPICTResourceHandler ) ; +#endif + AddHandler( new wxICONResourceHandler ) ; +} + +// ---------------------------------------------------------------------------- +// raw bitmap access support +// ---------------------------------------------------------------------------- + +void *wxBitmap::GetRawData(wxPixelDataBase& data, int WXUNUSED(bpp)) +{ + if ( !Ok() ) + // no bitmap, no data (raw or otherwise) + return NULL; + + data.m_width = GetWidth() ; + data.m_height = GetHeight() ; + data.m_stride = GetBitmapData()->GetBytesPerRow() ; + + return BeginRawAccess() ; +} + +void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(dataBase)) +{ + EndRawAccess() ; +} + +void wxBitmap::UseAlpha() +{ + // remember that we are using alpha channel: + // we'll need to create a proper mask in UngetRawData() + M_BITMAPDATA->UseAlpha( true ); +} diff --git a/src/osx/carbon/bmpbuttn.cpp b/src/osx/carbon/bmpbuttn.cpp new file mode 100644 index 0000000000..5757f50b84 --- /dev/null +++ b/src/osx/carbon/bmpbuttn.cpp @@ -0,0 +1,189 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/bmpbuttn.cpp +// Purpose: wxBitmapButton +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_BMPBUTTON + +#include "wx/bmpbuttn.h" +#include "wx/image.h" + +#ifndef WX_PRECOMP + #include "wx/dcmemory.h" +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxBitmapButton, wxButton) + +#include "wx/mac/uma.h" + +//--------------------------------------------------------------------------- +// Helper functions + +static wxBitmap wxMakeStdSizeBitmap(const wxBitmap& bitmap) +{ + // in Mac OS X the icon controls (which are used for borderless bitmap + // buttons) can have only one of the few standard sizes and if they + // don't, the OS rescales them automatically resulting in really ugly + // images, so centre the image in a square of standard size instead + + // the supported sizes, sorted in decreasng order + static const int stdSizes[] = { 128, 48, 32, 16, 0 }; + + const int width = bitmap.GetWidth(); + const int height = bitmap.GetHeight(); + + wxBitmap newBmp(bitmap); + + int n; + for ( n = 0; n < (int)WXSIZEOF(stdSizes); n++ ) + { + const int sizeStd = stdSizes[n]; + if ( width > sizeStd || height > sizeStd ) + { + // it will become -1 if the bitmap is larger than the biggest + // supported size, this is intentional + n--; + + break; + } + } + + if ( n != -1 ) + { + const int sizeStd = stdSizes[n]; + if ( width != sizeStd || height != sizeStd ) + { + wxASSERT_MSG( width <= sizeStd && height <= sizeStd, + _T("bitmap shouldn't be cropped") ); + + wxImage square_image = bitmap.ConvertToImage(); + newBmp = square_image.Size + ( + wxSize(sizeStd, sizeStd), + wxPoint((sizeStd - width)/2, (sizeStd-height)/2) + ); + } + } + //else: let the system rescale the bitmap + + return newBmp; +} + +//--------------------------------------------------------------------------- + +bool wxBitmapButton::Create( wxWindow *parent, + wxWindowID id, const wxBitmap& bitmap, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_macIsUserPane = false; + + // since bitmapbuttonbase is subclass of button calling wxBitmapButtonBase::Create + // essentially creates an additional button + if ( !wxControl::Create( parent, id, pos, size, style, validator, name ) ) + return false; + + if ( style & wxBU_AUTODRAW ) + { + m_marginX = + m_marginY = wxDEFAULT_BUTTON_MARGIN; + } + else + { + m_marginX = + m_marginY = 0; + } + + OSStatus err = noErr; + ControlButtonContentInfo info; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + m_peer = new wxMacControl( this ); + + if ( bitmap.Ok() && HasFlag(wxBORDER_NONE) ) + m_bmpNormal = wxMakeStdSizeBitmap(bitmap); + else + m_bmpNormal = bitmap; + + + if ( HasFlag( wxBORDER_NONE ) ) + { + // contrary to the docs this control only works with iconrefs + wxMacCreateBitmapButton( &info, m_bmpNormal, kControlContentIconRef ); + err = CreateIconControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), + &bounds, &info, false, m_peer->GetControlRefAddr() ); + } + else + { + wxMacCreateBitmapButton( &info, m_bmpNormal ); + err = CreateBevelButtonControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, CFSTR(""), + ((style & wxBU_AUTODRAW) ? kControlBevelButtonSmallBevel : kControlBevelButtonNormalBevel ), + kControlBehaviorOffsetContents, &info, 0, 0, 0, m_peer->GetControlRefAddr() ); + } + + verify_noerr( err ); + + wxMacReleaseBitmapButton( &info ); + wxASSERT_MSG( m_peer != NULL && m_peer->Ok(), wxT("No valid native Mac control") ); + + MacPostControlCreate( pos, size ); + + return true; +} + +void wxBitmapButton::SetBitmapLabel( const wxBitmap& bitmap ) +{ + if ( HasFlag( wxBORDER_NONE ) ) + m_bmpNormal = wxMakeStdSizeBitmap(bitmap); + else + m_bmpNormal = bitmap; + + InvalidateBestSize(); + + ControlButtonContentInfo info; + + if ( HasFlag( wxBORDER_NONE ) ) + { + wxMacCreateBitmapButton( &info, m_bmpNormal, kControlContentIconRef ); + if ( info.contentType != kControlNoContent ) + m_peer->SetData( kControlIconPart, kControlIconContentTag, info ); + } + else + { + wxMacCreateBitmapButton( &info, m_bmpNormal ); + if ( info.contentType != kControlNoContent ) + m_peer->SetData( kControlButtonPart, kControlBevelButtonContentTag, info ); + } + + wxMacReleaseBitmapButton( &info ); +} + +wxSize wxBitmapButton::DoGetBestSize() const +{ + wxSize best; + + best.x = 2 * m_marginX; + best.y = 2 * m_marginY; + if ( m_bmpNormal.Ok() ) + { + best.x += m_bmpNormal.GetWidth(); + best.y += m_bmpNormal.GetHeight(); + } + + return best; +} + +#endif diff --git a/src/osx/carbon/brush.cpp b/src/osx/carbon/brush.cpp new file mode 100644 index 0000000000..84dd60d6e9 --- /dev/null +++ b/src/osx/carbon/brush.cpp @@ -0,0 +1,192 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/brush.cpp +// Purpose: wxBrush +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/brush.h" + +#ifndef WX_PRECOMP + #include "wx/utils.h" +#endif + +#include "wx/mac/private.h" + +IMPLEMENT_DYNAMIC_CLASS(wxBrush, wxGDIObject) + +class WXDLLEXPORT wxBrushRefData: public wxGDIRefData +{ +public: + wxBrushRefData(const wxColour& colour = wxNullColour, wxBrushStyle style = wxBRUSHSTYLE_SOLID); + wxBrushRefData(const wxBitmap& stipple); + wxBrushRefData(const wxBrushRefData& data); + virtual ~wxBrushRefData(); + + bool operator==(const wxBrushRefData& data) const; + + const wxColour& GetColour() const { return m_colour; } + wxBrushStyle GetStyle() const { return m_style; } + wxBitmap *GetStipple() { return &m_stipple; } + + void SetColour(const wxColour& colour) { m_colour = colour; } + void SetStyle(wxBrushStyle style) { m_style = style; } + void SetStipple(const wxBitmap& stipple) { DoSetStipple(stipple); } + +protected: + void DoSetStipple(const wxBitmap& stipple); + + wxBitmap m_stipple; + wxColour m_colour; + wxBrushStyle m_style; +}; + +#define M_BRUSHDATA ((wxBrushRefData *)m_refData) + +wxBrushRefData::wxBrushRefData(const wxColour& colour, wxBrushStyle style) + : m_colour(colour), m_style( style ) +{ +} + +wxBrushRefData::wxBrushRefData(const wxBitmap& stipple) +{ + DoSetStipple( stipple ); +} + +wxBrushRefData::wxBrushRefData(const wxBrushRefData& data) + : wxGDIRefData() , + m_stipple(data.m_stipple), + m_colour(data.m_colour), + m_style(data.m_style) +{ +} + +wxBrushRefData::~wxBrushRefData() +{ +} + +bool wxBrushRefData::operator==(const wxBrushRefData& data) const +{ + return m_style == data.m_style && + m_colour == data.m_colour && + m_stipple.IsSameAs(data.m_stipple); +} + +void wxBrushRefData::DoSetStipple(const wxBitmap& stipple) +{ + m_stipple = stipple; + m_style = stipple.GetMask() ? wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE : wxBRUSHSTYLE_STIPPLE; +} +// +// +// + +wxBrush::wxBrush() +{ +} + +wxBrush::~wxBrush() +{ +} + +wxBrush::wxBrush(const wxColour& col, wxBrushStyle style) +{ + m_refData = new wxBrushRefData( col, style ); +} + +#if FUTURE_WXWIN_COMPATIBILITY_3_0 +wxBrush::wxBrush(const wxColour& col, int style) +{ + m_refData = new wxBrushRefData(col, (wxBrushStyle)style); +} +#endif + +wxBrush::wxBrush(const wxBitmap& stipple) +{ + m_refData = new wxBrushRefData( stipple ); +} + +// ---------------------------------------------------------------------------- +// wxBrush house keeping stuff +// ---------------------------------------------------------------------------- + +bool wxBrush::operator==(const wxBrush& brush) const +{ + const wxBrushRefData *brushData = (wxBrushRefData *)brush.m_refData; + + // an invalid brush is considered to be only equal to another invalid brush + return m_refData ? (brushData && *M_BRUSHDATA == *brushData) : !brushData; +} + +wxGDIRefData *wxBrush::CreateGDIRefData() const +{ + return new wxBrushRefData; +} + +wxGDIRefData *wxBrush::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxBrushRefData(*(const wxBrushRefData *)data); +} + +// ---------------------------------------------------------------------------- +// wxBrush accessors +// ---------------------------------------------------------------------------- + +wxColour wxBrush::GetColour() const +{ + wxCHECK_MSG( Ok(), wxNullColour, _T("invalid brush") ); + + return M_BRUSHDATA->GetColour(); +} + +wxBrushStyle wxBrush::GetStyle() const +{ + wxCHECK_MSG( Ok(), wxBRUSHSTYLE_INVALID, _T("invalid brush") ); + + return M_BRUSHDATA->GetStyle(); +} + +wxBitmap *wxBrush::GetStipple() const +{ + wxCHECK_MSG( Ok(), NULL, _T("invalid brush") ); + + return M_BRUSHDATA->GetStipple(); +} + +// ---------------------------------------------------------------------------- +// wxBrush setters +// ---------------------------------------------------------------------------- + +void wxBrush::SetColour(const wxColour& col) +{ + AllocExclusive(); + + M_BRUSHDATA->SetColour(col); +} + +void wxBrush::SetColour(unsigned char r, unsigned char g, unsigned char b) +{ + AllocExclusive(); + + M_BRUSHDATA->SetColour(wxColour(r, g, b)); +} + +void wxBrush::SetStyle(wxBrushStyle style) +{ + AllocExclusive(); + + M_BRUSHDATA->SetStyle(style); +} + +void wxBrush::SetStipple(const wxBitmap& stipple) +{ + AllocExclusive(); + + M_BRUSHDATA->SetStipple(stipple); +} diff --git a/src/osx/carbon/button.cpp b/src/osx/carbon/button.cpp new file mode 100644 index 0000000000..87fa56c82b --- /dev/null +++ b/src/osx/carbon/button.cpp @@ -0,0 +1,273 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/button.cpp +// Purpose: wxButton +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/button.h" + +#ifndef WX_PRECOMP + #include "wx/panel.h" + #include "wx/toplevel.h" + #include "wx/dcclient.h" +#endif + +#include "wx/stockitem.h" + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl) + + +bool wxButton::Create(wxWindow *parent, + wxWindowID id, + const wxString& lbl, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + wxString label(lbl); + if (label.empty() && wxIsStockID(id)) + label = wxGetStockLabel(id); + + m_macIsUserPane = false ; + + if ( !wxButtonBase::Create(parent, id, pos, size, style, validator, name) ) + return false; + + m_labelOrig = m_label = label ; + + OSStatus err; + Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ; + m_peer = new wxMacControl(this) ; + if ( id == wxID_HELP ) + { + ControlButtonContentInfo info ; + info.contentType = kControlContentIconRef ; + GetIconRef(kOnSystemDisk, kSystemIconsCreator, kHelpIcon, &info.u.iconRef); + err = CreateRoundButtonControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), + &bounds, kControlRoundButtonNormalSize, + &info, m_peer->GetControlRefAddr() ); + } + else if ( label.Find('\n' ) == wxNOT_FOUND && label.Find('\r' ) == wxNOT_FOUND) + { + // Button height is static in Mac, can't be changed, so we need to force it here + if ( GetWindowVariant() == wxWINDOW_VARIANT_NORMAL || GetWindowVariant() == wxWINDOW_VARIANT_LARGE ) + { + bounds.bottom = bounds.top + 20 ; + m_maxHeight = 20 ; + } + else if ( GetWindowVariant() == wxWINDOW_VARIANT_SMALL ) + { + bounds.bottom = bounds.top + 17 ; + m_maxHeight = 17 ; + } + else if ( GetWindowVariant() == wxWINDOW_VARIANT_MINI ) + { + bounds.bottom = bounds.top + 15 ; + m_maxHeight = 15 ; + } + err = CreatePushButtonControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), + &bounds, CFSTR(""), m_peer->GetControlRefAddr() ); + } + else + { + ControlButtonContentInfo info ; + info.contentType = kControlNoContent ; + err = CreateBevelButtonControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()) , &bounds, CFSTR(""), + kControlBevelButtonLargeBevel, kControlBehaviorPushbutton, + &info, 0, 0, 0, m_peer->GetControlRefAddr() ); + } + + verify_noerr( err ); + wxASSERT_MSG( m_peer != NULL && m_peer->Ok() , wxT("No valid Mac control") ) ; + + MacPostControlCreate( pos, size ); + + return true; +} + +wxWindow *wxButton::SetDefault() +{ + wxWindow *btnOldDefault = wxButtonBase::SetDefault(); + + if ( btnOldDefault ) + { + // cast needed to access the protected member + btnOldDefault->GetPeer()->SetData(kControlButtonPart , kControlPushButtonDefaultTag , (Boolean) 0 ) ; + } + + m_peer->SetData(kControlButtonPart , kControlPushButtonDefaultTag , (Boolean) 1 ) ; + + return btnOldDefault; +} + +wxSize wxButton::DoGetBestSize() const +{ + if ( GetId() == wxID_HELP ) + return wxSize( 20 , 20 ) ; + + wxSize sz = GetDefaultSize() ; + + switch (GetWindowVariant()) + { + case wxWINDOW_VARIANT_NORMAL: + case wxWINDOW_VARIANT_LARGE: + sz.y = 20 ; + break; + + case wxWINDOW_VARIANT_SMALL: + sz.y = 17 ; + break; + + case wxWINDOW_VARIANT_MINI: + sz.y = 15 ; + break; + + default: + break; + } + + Rect bestsize = { 0 , 0 , 0 , 0 } ; + m_peer->GetBestRect( &bestsize ) ; + + int wBtn; + if ( EmptyRect( &bestsize ) || ( GetWindowStyle() & wxBU_EXACTFIT) ) + { + Point bounds; + + ControlFontStyleRec controlFont; + OSStatus err = m_peer->GetData( kControlEntireControl, kControlFontStyleTag, &controlFont ); + verify_noerr( err ); + + wxCFStringRef str( m_label, GetFont().GetEncoding() ); + +#if wxMAC_USE_ATSU_TEXT + SInt16 baseline; + if ( m_font.MacGetThemeFontID() != kThemeCurrentPortFont ) + { + err = GetThemeTextDimensions( + (!m_label.empty() ? (CFStringRef)str : CFSTR(" ")), + m_font.MacGetThemeFontID(), kThemeStateActive, false, &bounds, &baseline ); + verify_noerr( err ); + } + else +#endif + { + wxClientDC dc(const_cast(this)); + wxCoord width, height ; + dc.GetTextExtent( m_label , &width, &height); + bounds.h = width; + bounds.v = height; + } + + wBtn = bounds.h + sz.y; + } + else + { + wBtn = bestsize.right - bestsize.left ; + // non 'normal' window variants don't return the correct height + // sz.y = bestsize.bottom - bestsize.top ; + } + + if ((wBtn > sz.x) || ( GetWindowStyle() & wxBU_EXACTFIT)) + sz.x = wBtn; + + return sz ; +} + +wxSize wxButton::GetDefaultSize() +{ + int wBtn = 70 ; + int hBtn = 20 ; + + return wxSize(wBtn, hBtn); +} + +void wxButton::Command (wxCommandEvent & event) +{ + m_peer->Flash(kControlButtonPart) ; + ProcessCommand(event); +} + +wxInt32 wxButton::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, m_windowId); + event.SetEventObject(this); + ProcessCommand(event); + + return noErr; +} + +//------------------------------------------------------- +// wxDisclosureTriangle +//------------------------------------------------------- + +bool wxDisclosureTriangle::Create(wxWindow *parent, wxWindowID id, const wxString& label, + const wxPoint& pos, const wxSize& size, long style,const wxValidator& validator, const wxString& name ) +{ + m_macIsUserPane = false ; + + if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ; + m_peer = new wxMacControl(this) ; + + OSStatus err = CreateDisclosureTriangleControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()) , &bounds, + kControlDisclosureTrianglePointDefault, + wxCFStringRef( label ), + 0, // closed + TRUE, // draw title + TRUE, // auto toggle back and forth + m_peer->GetControlRefAddr() ); + + verify_noerr( err ); + wxASSERT_MSG( m_peer != NULL && m_peer->Ok() , wxT("No valid Mac control") ) ; + + MacPostControlCreate( pos, size ); + // passing the text in the param doesn't seem to work, so lets do if again + SetLabel( label ); + + return true; +} + +void wxDisclosureTriangle::SetOpen( bool open ) +{ + m_peer->SetValue( open ? 1 : 0 ); +} + +bool wxDisclosureTriangle::IsOpen() const +{ + return m_peer->GetValue() == 1; +} + +wxInt32 wxDisclosureTriangle::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + // Just emit button event for now + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, m_windowId); + event.SetEventObject(this); + ProcessCommand(event); + + return noErr; +} + +wxSize wxDisclosureTriangle::DoGetBestSize() const +{ + return wxSize(16,16); +} + + diff --git a/src/osx/carbon/carbrsrc.r b/src/osx/carbon/carbrsrc.r new file mode 100644 index 0000000000..9326cc3b56 --- /dev/null +++ b/src/osx/carbon/carbrsrc.r @@ -0,0 +1,7 @@ +// carbon for 9 +data 'carb' (0) { + $"0000" /* .. */ +}; + +// the plist resource should only be included in the application +// since it contains the bundle information and should not be duplicated diff --git a/src/osx/carbon/cfsocket.cpp b/src/osx/carbon/cfsocket.cpp new file mode 100644 index 0000000000..8a2383c7f5 --- /dev/null +++ b/src/osx/carbon/cfsocket.cpp @@ -0,0 +1,2311 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/cfsocket.cpp +// Purpose: Socket handler classes +// Authors: Guilhem Lavaux, Guillermo Rodriguez Garcia +// Created: April 1997 +// Copyright: (C) 1999-1997, Guilhem Lavaux +// (C) 2000-1999, Guillermo Rodriguez Garcia +// RCS_ID: $Id$ +// License: see wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_SOCKETS + +#include "wx/socket.h" + +#ifndef WX_PRECOMP + #include "wx/object.h" + #include "wx/string.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/event.h" + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/timer.h" + #include "wx/module.h" +#endif + +#include "wx/apptrait.h" + +#include "wx/sckaddr.h" +#include "wx/mac/carbon/private.h" + +#include +#include +#include +#include + +#define HAVE_INET_ATON + +// DLL options compatibility check: +#include "wx/build.h" + +WX_CHECK_BUILD_OPTIONS("wxNet") + + +// discard buffer +#define MAX_DISCARD_SIZE (10 * 1024) + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif + +// what to do within waits: we have 2 cases: from the main thread itself we +// have to call wxYield() to let the events (including the GUI events and the +// low-level (not wxWidgets) events from GSocket) be processed. From another +// thread it is enough to just call wxThread::Yield() which will give away the +// rest of our time slice: the explanation is that the events will be processed +// by the main thread anyhow, without calling wxYield(), but we don't want to +// eat the CPU time uselessly while sitting in the loop waiting for the data +#if wxUSE_THREADS + #define PROCESS_EVENTS() \ + { \ + if ( wxThread::IsMain() ) \ + wxYield(); \ + else \ + wxThread::Yield(); \ + } +#else // !wxUSE_THREADS + #define PROCESS_EVENTS() wxYield() +#endif // wxUSE_THREADS/!wxUSE_THREADS + +#define wxTRACE_Socket _T("wxSocket") + + +IMPLEMENT_CLASS(wxSocketBase, wxObject) +IMPLEMENT_CLASS(wxSocketServer, wxSocketBase) +IMPLEMENT_CLASS(wxSocketClient, wxSocketBase) +IMPLEMENT_CLASS(wxDatagramSocket, wxSocketBase) +IMPLEMENT_DYNAMIC_CLASS(wxSocketEvent, wxEvent) + +// -------------------------------------------------------------------------- +// private classes +// -------------------------------------------------------------------------- + +class wxSocketState : public wxObject +{ +public: + wxSocketFlags m_flags; + wxSocketEventFlags m_eventmask; + bool m_notify; + void *m_clientData; + +public: + wxSocketState() : wxObject() {} + + DECLARE_NO_COPY_CLASS(wxSocketState) +}; + +struct _GSocket +{ + CFSocketNativeHandle m_fd; + GAddress *m_local; + GAddress *m_peer; + GSocketError m_error; + + int m_non_blocking; + int m_server; + int m_stream; + int m_oriented; + int m_establishing; + unsigned long m_timeout; + + // Callbacks + GSocketEventFlags m_detected; + GSocketCallback m_cbacks[GSOCK_MAX_EVENT]; + char *m_data[GSOCK_MAX_EVENT]; + + CFSocketRef m_cfSocket; + CFRunLoopSourceRef m_runLoopSource; + CFReadStreamRef m_readStream ; + CFWriteStreamRef m_writeStream ; +}; + +struct _GAddress +{ + struct sockaddr *m_addr; + size_t m_len; + + GAddressType m_family; + int m_realfamily; + + GSocketError m_error; + int somethingElse ; +}; + +void wxMacCFSocketCallback(CFSocketRef s, CFSocketCallBackType callbackType, + CFDataRef address, const void* data, void* info) ; +void _GSocket_Enable(GSocket *socket, GSocketEvent event) ; +void _GSocket_Disable(GSocket *socket, GSocketEvent event) ; + +// ========================================================================== +// wxSocketBase +// ========================================================================== + +// -------------------------------------------------------------------------- +// Initialization and shutdown +// -------------------------------------------------------------------------- + +// FIXME-MT: all this is MT-unsafe, of course, we should protect all accesses +// to m_countInit with a crit section +size_t wxSocketBase::m_countInit = 0; + +bool wxSocketBase::IsInitialized() +{ + return m_countInit > 0; +} + +bool wxSocketBase::Initialize() +{ + if ( !m_countInit++ ) + { +#if 0 + wxAppTraits *traits = wxAppConsole::GetInstance() ? + wxAppConsole::GetInstance()->GetTraits() : NULL; + GSocketGUIFunctionsTable *functions = + traits ? traits->GetSocketGUIFunctionsTable() : NULL; + GSocket_SetGUIFunctions(functions); + + if ( !GSocket_Init() ) + { + m_countInit--; + + return false; + } +#endif + } + + return true; +} + +void wxSocketBase::Shutdown() +{ + // we should be initialized + wxASSERT_MSG( m_countInit, wxT("extra call to Shutdown()") ); + if ( !--m_countInit ) + { +#if 0 + GSocket_Cleanup(); +#endif + } +} + +// -------------------------------------------------------------------------- +// Ctor and dtor +// -------------------------------------------------------------------------- + +void wxSocketBase::Init() +{ + m_socket = NULL; + m_type = wxSOCKET_UNINIT; + + // state + m_flags = 0; + m_connected = + m_establishing = + m_reading = + m_writing = + m_error = false; + m_lcount = 0; + m_timeout = 600; + m_beingDeleted = false; + + // pushback buffer + m_unread = NULL; + m_unrd_size = 0; + m_unrd_cur = 0; + + // events + m_id = -1; + m_handler = NULL; + m_clientData = NULL; + m_notify = false; + m_eventmask = 0; + + if ( !IsInitialized() ) + { + // this Initialize() will be undone by wxSocketModule::OnExit(), all the + // other calls to it should be matched by a call to Shutdown() + Initialize(); + } +} + +wxSocketBase::wxSocketBase() +{ + Init(); +} + +wxSocketBase::wxSocketBase( wxSocketFlags flags, wxSocketType type) +{ + Init(); + + m_flags = flags; + m_type = type; +} + +wxSocketBase::~wxSocketBase() +{ + // Just in case the app called Destroy() *and* then deleted + // the socket immediately: don't leave dangling pointers. + wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + if ( traits ) + traits->RemoveFromPendingDelete(this); + + // Shutdown and close the socket + if (!m_beingDeleted) + Close(); + + // Destroy the GSocket object + if (m_socket) + { + GSocket_destroy(m_socket); + } + + // Free the pushback buffer + if (m_unread) + free(m_unread); +} + +bool wxSocketBase::Destroy() +{ + // Delayed destruction: the socket will be deleted during the next + // idle loop iteration. This ensures that all pending events have + // been processed. + m_beingDeleted = true; + + // Shutdown and close the socket + Close(); + + // Supress events from now on + Notify(false); + + // schedule this object for deletion + wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + if ( traits ) + { + // let the traits object decide what to do with us + traits->ScheduleForDestroy(this); + } + else // no app or no traits + { + // in wxBase we might have no app object at all, don't leak memory + delete this; + } + + return true; +} + +// -------------------------------------------------------------------------- +// Basic IO calls +// -------------------------------------------------------------------------- + +// The following IO operations update m_error and m_lcount: +// {Read, Write, ReadMsg, WriteMsg, Peek, Unread, Discard} +// +// TODO: Should Connect, Accept and AcceptWith update m_error? + +bool wxSocketBase::Close() +{ + // Interrupt pending waits + InterruptWait(); + + if (m_socket) + GSocket_Shutdown(m_socket); + + m_connected = false; + m_establishing = false; + + return true; +} + +wxSocketBase& wxSocketBase::Read(void* buffer, wxUint32 nbytes) +{ + // Mask read events + m_reading = true; + + m_lcount = _Read(buffer, nbytes); + + // If in wxSOCKET_WAITALL mode, all bytes should have been read. + if (m_flags & wxSOCKET_WAITALL) + m_error = (m_lcount != nbytes); + else + m_error = (m_lcount == 0); + + // Allow read events from now on + m_reading = false; + + return *this; +} + +wxUint32 wxSocketBase::_Read(void* buffer, wxUint32 nbytes) +{ + int total = 0; + + // Try the pushback buffer first + total = GetPushback(buffer, nbytes, false); + nbytes -= total; + buffer = (char *)buffer + total; + + // Return now in one of the following cases: + // - the socket is invalid, + // - we got all the data, + // - we got *some* data and we are not using wxSOCKET_WAITALL. + if ( !m_socket || + !nbytes || + ((total != 0) && !(m_flags & wxSOCKET_WAITALL)) ) + return total; + + // Possible combinations (they are checked in this order) + // wxSOCKET_NOWAIT + // wxSOCKET_WAITALL (with or without wxSOCKET_BLOCK) + // wxSOCKET_BLOCK + // wxSOCKET_NONE + // + + int ret; + if (m_flags & wxSOCKET_NOWAIT) + { + GSocket_SetNonBlocking(m_socket, 1); + ret = GSocket_Read(m_socket, (char *)buffer, nbytes); + GSocket_SetNonBlocking(m_socket, 0); + + if (ret > 0) + total += ret; + } + else + { + bool more = true; + + while (more) + { + if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForRead() ) + break; + + ret = GSocket_Read(m_socket, (char *)buffer, nbytes); + + if (ret > 0) + { + total += ret; + nbytes -= ret; + buffer = (char *)buffer + ret; + } + + // If we got here and wxSOCKET_WAITALL is not set, we can leave + // now. Otherwise, wait until we recv all the data or until there + // is an error. + // + more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL)); + } + } + + return total; +} + +wxSocketBase& wxSocketBase::ReadMsg(void* buffer, wxUint32 nbytes) +{ + wxUint32 len, len2, sig, total; + bool error; + int old_flags; + struct + { + unsigned char sig[4]; + unsigned char len[4]; + } + msg; + + // Mask read events + m_reading = true; + + total = 0; + error = true; + old_flags = m_flags; + SetFlags((m_flags & wxSOCKET_BLOCK) | wxSOCKET_WAITALL); + + if (_Read(&msg, sizeof(msg)) != sizeof(msg)) + goto exit; + + sig = (wxUint32)msg.sig[0]; + sig |= (wxUint32)(msg.sig[1] << 8); + sig |= (wxUint32)(msg.sig[2] << 16); + sig |= (wxUint32)(msg.sig[3] << 24); + + if (sig != 0xfeeddead) + { + wxLogWarning( wxT("wxSocket: invalid signature in ReadMsg.") ); + goto exit; + } + + len = (wxUint32)msg.len[0]; + len |= (wxUint32)(msg.len[1] << 8); + len |= (wxUint32)(msg.len[2] << 16); + len |= (wxUint32)(msg.len[3] << 24); + + if (len > nbytes) + { + len2 = len - nbytes; + len = nbytes; + } + else + len2 = 0; + + // Don't attemp to read if the msg was zero bytes long. + if (len) + { + total = _Read(buffer, len); + + if (total != len) + goto exit; + } + if (len2) + { + char *discard_buffer = new char[MAX_DISCARD_SIZE]; + long discard_len; + + // NOTE: discarded bytes don't add to m_lcount. + do + { + discard_len = ((len2 > MAX_DISCARD_SIZE)? MAX_DISCARD_SIZE : len2); + discard_len = _Read(discard_buffer, (wxUint32)discard_len); + len2 -= (wxUint32)discard_len; + } + while ((discard_len > 0) && len2); + + delete [] discard_buffer; + + if (len2 != 0) + goto exit; + } + if (_Read(&msg, sizeof(msg)) != sizeof(msg)) + goto exit; + + sig = (wxUint32)msg.sig[0]; + sig |= (wxUint32)(msg.sig[1] << 8); + sig |= (wxUint32)(msg.sig[2] << 16); + sig |= (wxUint32)(msg.sig[3] << 24); + + if (sig != 0xdeadfeed) + { + wxLogWarning( wxT("wxSocket: invalid signature in ReadMsg.") ); + goto exit; + } + + // everything was OK + error = false; + +exit: + m_error = error; + m_lcount = total; + m_reading = false; + SetFlags(old_flags); + + return *this; +} + +wxSocketBase& wxSocketBase::Peek(void* buffer, wxUint32 nbytes) +{ + // Mask read events + m_reading = true; + + m_lcount = _Read(buffer, nbytes); + Pushback(buffer, m_lcount); + + // If in wxSOCKET_WAITALL mode, all bytes should have been read. + if (m_flags & wxSOCKET_WAITALL) + m_error = (m_lcount != nbytes); + else + m_error = (m_lcount == 0); + + // Allow read events again + m_reading = false; + + return *this; +} + +wxSocketBase& wxSocketBase::Write(const void *buffer, wxUint32 nbytes) +{ + // Mask write events + m_writing = true; + + m_lcount = _Write(buffer, nbytes); + + // If in wxSOCKET_WAITALL mode, all bytes should have been written. + if (m_flags & wxSOCKET_WAITALL) + m_error = (m_lcount != nbytes); + else + m_error = (m_lcount == 0); + + // Allow write events again + m_writing = false; + + return *this; +} + +wxUint32 wxSocketBase::_Write(const void *buffer, wxUint32 nbytes) +{ + wxUint32 total = 0; + + // If the socket is invalid or parameters are ill, return immediately + if (!m_socket || !buffer || !nbytes) + return 0; + + // Possible combinations (they are checked in this order) + // wxSOCKET_NOWAIT + // wxSOCKET_WAITALL (with or without wxSOCKET_BLOCK) + // wxSOCKET_BLOCK + // wxSOCKET_NONE + // + int ret; + if (m_flags & wxSOCKET_NOWAIT) + { + GSocket_SetNonBlocking(m_socket, 1); + ret = GSocket_Write(m_socket, (const char *)buffer, nbytes); + GSocket_SetNonBlocking(m_socket, 0); + + if (ret > 0) + total = ret; + } + else + { + bool more = true; + + while (more) + { + if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForWrite() ) + break; + + ret = GSocket_Write(m_socket, (const char *)buffer, nbytes); + + if (ret > 0) + { + total += ret; + nbytes -= ret; + buffer = (const char *)buffer + ret; + } + + // If we got here and wxSOCKET_WAITALL is not set, we can leave + // now. Otherwise, wait until we send all the data or until there + // is an error. + // + more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL)); + } + } + + return total; +} + +wxSocketBase& wxSocketBase::WriteMsg(const void *buffer, wxUint32 nbytes) +{ + wxUint32 total; + bool error; + struct + { + unsigned char sig[4]; + unsigned char len[4]; + } + msg; + + // Mask write events + m_writing = true; + + error = true; + total = 0; + SetFlags((m_flags & wxSOCKET_BLOCK) | wxSOCKET_WAITALL); + + msg.sig[0] = (unsigned char) 0xad; + msg.sig[1] = (unsigned char) 0xde; + msg.sig[2] = (unsigned char) 0xed; + msg.sig[3] = (unsigned char) 0xfe; + + msg.len[0] = (unsigned char) (nbytes & 0xff); + msg.len[1] = (unsigned char) ((nbytes >> 8) & 0xff); + msg.len[2] = (unsigned char) ((nbytes >> 16) & 0xff); + msg.len[3] = (unsigned char) ((nbytes >> 24) & 0xff); + + if (_Write(&msg, sizeof(msg)) < sizeof(msg)) + goto exit; + + total = _Write(buffer, nbytes); + + if (total < nbytes) + goto exit; + + msg.sig[0] = (unsigned char) 0xed; + msg.sig[1] = (unsigned char) 0xfe; + msg.sig[2] = (unsigned char) 0xad; + msg.sig[3] = (unsigned char) 0xde; + msg.len[0] = msg.len[1] = msg.len[2] = msg.len[3] = (char) 0; + + if ((_Write(&msg, sizeof(msg))) < sizeof(msg)) + goto exit; + + // everything was OK + error = false; + +exit: + m_error = error; + m_lcount = total; + m_writing = false; + + return *this; +} + +wxSocketBase& wxSocketBase::Unread(const void *buffer, wxUint32 nbytes) +{ + if (nbytes != 0) + Pushback(buffer, nbytes); + + m_error = false; + m_lcount = nbytes; + + return *this; +} + +wxSocketBase& wxSocketBase::Discard() +{ + char *buffer = new char[MAX_DISCARD_SIZE]; + wxUint32 ret; + wxUint32 total = 0; + + // Mask read events + m_reading = true; + + SetFlags(wxSOCKET_NOWAIT); + + do + { + ret = _Read(buffer, MAX_DISCARD_SIZE); + total += ret; + } + while (ret == MAX_DISCARD_SIZE); + + delete[] buffer; + m_lcount = total; + m_error = false; + + // Allow read events again + m_reading = false; + + return *this; +} + +// -------------------------------------------------------------------------- +// Wait functions +// -------------------------------------------------------------------------- + +// All Wait functions poll the socket using GSocket_Select() to +// check for the specified combination of conditions, until one +// of these conditions become true, an error occurs, or the +// timeout elapses. The polling loop calls PROCESS_EVENTS(), so +// this won't block the GUI. + +bool wxSocketBase::_Wait(long seconds, + long milliseconds, + wxSocketEventFlags flags) +{ + GSocketEventFlags result; + long timeout; + + // Set this to true to interrupt ongoing waits + m_interrupt = false; + + // Check for valid socket + if (!m_socket) + return false; + + // Check for valid timeout value. + if (seconds != -1) + timeout = seconds * 1000 + milliseconds; + else + timeout = m_timeout * 1000; + +#if !defined(wxUSE_GUI) || !wxUSE_GUI + GSocket_SetTimeout(m_socket, timeout); +#endif + + // Wait in an active polling loop. + // + // NOTE: We duplicate some of the code in OnRequest, but this doesn't + // hurt. It has to be here because the (GSocket) event might arrive + // a bit delayed, and it has to be in OnRequest as well because we + // don't know whether the Wait functions are being used. + // + // Do this at least once (important if timeout == 0, when + // we are just polling). Also, if just polling, do not yield. + + wxStopWatch chrono; + bool done = false; + + while (!done) + { + result = GSocket_Select(m_socket, flags | GSOCK_LOST_FLAG); + + // Incoming connection (server) or connection established (client) + if (result & GSOCK_CONNECTION_FLAG) + { + m_connected = true; + m_establishing = false; + + return true; + } + + // Data available or output buffer ready + if ((result & GSOCK_INPUT_FLAG) || (result & GSOCK_OUTPUT_FLAG)) + { + return true; + } + + // Connection lost + if (result & GSOCK_LOST_FLAG) + { + m_connected = false; + m_establishing = false; + + return (flags & GSOCK_LOST_FLAG) != 0; + } + + // Wait more? + if ((!timeout) || (chrono.Time() > timeout) || (m_interrupt)) + done = true; + else + PROCESS_EVENTS(); + } + + return false; +} + +bool wxSocketBase::Wait(long seconds, long milliseconds) +{ + return _Wait(seconds, milliseconds, GSOCK_INPUT_FLAG | + GSOCK_OUTPUT_FLAG | + GSOCK_CONNECTION_FLAG | + GSOCK_LOST_FLAG); +} + +bool wxSocketBase::WaitForRead(long seconds, long milliseconds) +{ + // Check pushback buffer before entering _Wait + if (m_unread) + return true; + + // Note that GSOCK_INPUT_LOST has to be explicitly passed to + // _Wait becuase of the semantics of WaitForRead: a return + // value of true means that a GSocket_Read call will return + // immediately, not that there is actually data to read. + + return _Wait(seconds, milliseconds, GSOCK_INPUT_FLAG | GSOCK_LOST_FLAG); +} + +bool wxSocketBase::WaitForWrite(long seconds, long milliseconds) +{ + return _Wait(seconds, milliseconds, GSOCK_OUTPUT_FLAG); +} + +bool wxSocketBase::WaitForLost(long seconds, long milliseconds) +{ + return _Wait(seconds, milliseconds, GSOCK_LOST_FLAG); +} + +// -------------------------------------------------------------------------- +// Miscellaneous +// -------------------------------------------------------------------------- + +// +// Get local or peer address +// + +bool wxSocketBase::GetPeer(wxSockAddress& addr_man) const +{ + GAddress *peer; + + if (!m_socket) + return false; + + peer = GSocket_GetPeer(m_socket); + + // copying a null address would just trigger an assert anyway + + if (!peer) + return false; + + addr_man.SetAddress(peer); + GAddress_destroy(peer); + + return true; +} + +bool wxSocketBase::GetLocal(wxSockAddress& addr_man) const +{ +#if 0 + GAddress *local; + + if (!m_socket) + return false; + + local = GSocket_GetLocal(m_socket); + addr_man.SetAddress(local); + GAddress_destroy(local); +#endif + + return true; +} + +// +// Save and restore socket state +// + +void wxSocketBase::SaveState() +{ + wxSocketState *state; + + state = new wxSocketState(); + + state->m_flags = m_flags; + state->m_notify = m_notify; + state->m_eventmask = m_eventmask; + state->m_clientData = m_clientData; + + m_states.Append(state); +} + +void wxSocketBase::RestoreState() +{ + wxList::compatibility_iterator node; + wxSocketState *state; + + node = m_states.GetLast(); + if (!node) + return; + + state = (wxSocketState *)node->GetData(); + + m_flags = state->m_flags; + m_notify = state->m_notify; + m_eventmask = state->m_eventmask; + m_clientData = state->m_clientData; + + m_states.Erase(node); + delete state; +} + +// +// Timeout and flags +// + +void wxSocketBase::SetTimeout(long seconds) +{ + m_timeout = seconds; + +#if 0 + if (m_socket) + GSocket_SetTimeout(m_socket, m_timeout * 1000); +#endif +} + +void wxSocketBase::SetFlags(wxSocketFlags flags) +{ + m_flags = flags; +} + + +// -------------------------------------------------------------------------- +// Event handling +// -------------------------------------------------------------------------- + +// A note on how events are processed, which is probably the most +// difficult thing to get working right while keeping the same API +// and functionality for all platforms. +// +// When GSocket detects an event, it calls wx_socket_callback, which in +// turn just calls wxSocketBase::OnRequest in the corresponding wxSocket +// object. OnRequest does some housekeeping, and if the event is to be +// propagated to the user, it creates a new wxSocketEvent object and +// posts it. The event is not processed immediately, but delayed with +// AddPendingEvent instead. This is necessary in order to decouple the +// event processing from wx_socket_callback; otherwise, subsequent IO +// calls made from the user event handler would fail, as gtk callbacks +// are not reentrant. +// +// Note that, unlike events, user callbacks (now deprecated) are _not_ +// decoupled from wx_socket_callback and thus they suffer from a variety +// of problems. Avoid them where possible and use events instead. + +extern "C" +void LINKAGEMODE wx_socket_callback(GSocket * WXUNUSED(socket), + GSocketEvent notification, + char *cdata) +{ + wxSocketBase *sckobj = (wxSocketBase *)cdata; + + sckobj->OnRequest((wxSocketNotify) notification); +} + +void wxSocketBase::OnRequest(wxSocketNotify notification) +{ + // NOTE: We duplicate some of the code in _Wait, but this doesn't + // hurt. It has to be here because the (GSocket) event might arrive + // a bit delayed, and it has to be in _Wait as well because we don't + // know whether the Wait functions are being used. + + switch (notification) + { + case wxSOCKET_CONNECTION: + m_establishing = false; + m_connected = true; + break; + + // If we are in the middle of a R/W operation, do not + // propagate events to users. Also, filter 'late' events + // which are no longer valid. + + case wxSOCKET_INPUT: + if (m_reading || !GSocket_Select(m_socket, GSOCK_INPUT_FLAG)) + return; + break; + + case wxSOCKET_OUTPUT: + if (m_writing || !GSocket_Select(m_socket, GSOCK_OUTPUT_FLAG)) + return; + break; + + case wxSOCKET_LOST: + m_connected = false; + m_establishing = false; + break; + + default: + break; + } + + // Schedule the event + + wxSocketEventFlags flag = 0; + wxUnusedVar(flag); + switch (notification) + { + case GSOCK_INPUT: + flag = GSOCK_INPUT_FLAG; + break; + + case GSOCK_OUTPUT: + flag = GSOCK_OUTPUT_FLAG; + break; + + case GSOCK_CONNECTION: + flag = GSOCK_CONNECTION_FLAG; + break; + + case GSOCK_LOST: + flag = GSOCK_LOST_FLAG; + break; + + default: + wxLogWarning( wxT("wxSocket: unknown event!") ); + return; + } + + if (((m_eventmask & flag) == flag) && m_notify) + { + if (m_handler) + { + wxSocketEvent event(m_id); + event.m_event = notification; + event.m_clientData = m_clientData; + event.SetEventObject(this); + + m_handler->AddPendingEvent(event); + } + } +} + +void wxSocketBase::Notify(bool notify) +{ + m_notify = notify; +} + +void wxSocketBase::SetNotify(wxSocketEventFlags flags) +{ + m_eventmask = flags; +} + +void wxSocketBase::SetEventHandler(wxEvtHandler& handler, int id) +{ + m_handler = &handler; + m_id = id; +} + +// -------------------------------------------------------------------------- +// Pushback buffer +// -------------------------------------------------------------------------- + +void wxSocketBase::Pushback(const void *buffer, wxUint32 size) +{ + if (!size) + return; + + if (m_unread == NULL) + m_unread = malloc(size); + else + { + void *tmp; + + tmp = malloc(m_unrd_size + size); + memcpy((char *)tmp + size, m_unread, m_unrd_size); + free(m_unread); + + m_unread = tmp; + } + + m_unrd_size += size; + + memcpy(m_unread, buffer, size); +} + +wxUint32 wxSocketBase::GetPushback(void *buffer, wxUint32 size, bool peek) +{ + if (!m_unrd_size) + return 0; + + if (size > (m_unrd_size-m_unrd_cur)) + size = m_unrd_size-m_unrd_cur; + + memcpy(buffer, (char *)m_unread + m_unrd_cur, size); + + if (!peek) + { + m_unrd_cur += size; + if (m_unrd_size == m_unrd_cur) + { + free(m_unread); + m_unread = NULL; + m_unrd_size = 0; + m_unrd_cur = 0; + } + } + + return size; +} + + +// ========================================================================== +// wxSocketServer +// ========================================================================== + +// -------------------------------------------------------------------------- +// Ctor +// -------------------------------------------------------------------------- + +wxSocketServer::wxSocketServer(wxSockAddress& addr_man, + wxSocketFlags flags) + : wxSocketBase(flags, wxSOCKET_SERVER) +{ + wxLogTrace( wxTRACE_Socket, wxT("Opening wxSocketServer") ); + + m_socket = GSocket_new(); + + if (!m_socket) + { + wxLogTrace( wxTRACE_Socket, wxT("*** GSocket_new failed") ); + return; + } + + // Setup the socket as server + +#if 0 + GSocket_SetLocal(m_socket, addr_man.GetAddress()); + if (GSocket_SetServer(m_socket) != GSOCK_NOERROR) + { + GSocket_destroy(m_socket); + m_socket = NULL; + + wxLogTrace( wxTRACE_Socket, wxT("*** GSocket_SetServer failed") ); + return; + } + + GSocket_SetTimeout(m_socket, m_timeout * 1000); + GSocket_SetCallback(m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG | + GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG, + wx_socket_callback, (char *)this); +#endif +} + +// -------------------------------------------------------------------------- +// Accept +// -------------------------------------------------------------------------- + +bool wxSocketServer::AcceptWith(wxSocketBase& sock, bool wait) +{ + GSocket *child_socket; + + if (!m_socket) + return false; + + // If wait == false, then the call should be nonblocking. + // When we are finished, we put the socket to blocking mode + // again. + +#if 0 + if (!wait) + GSocket_SetNonBlocking(m_socket, 1); + + child_socket = GSocket_WaitConnection(m_socket); + + if (!wait) + GSocket_SetNonBlocking(m_socket, 0); + + if (!child_socket) + return false; + + sock.m_type = wxSOCKET_BASE; + sock.m_socket = child_socket; + sock.m_connected = true; + + GSocket_SetTimeout(sock.m_socket, sock.m_timeout * 1000); + GSocket_SetCallback(sock.m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG | + GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG, + wx_socket_callback, (char *)&sock); +#endif + + return true; +} + +wxSocketBase *wxSocketServer::Accept(bool wait) +{ + wxSocketBase* sock = new wxSocketBase(); + + sock->SetFlags(m_flags); + + if (!AcceptWith(*sock, wait)) + { + sock->Destroy(); + sock = NULL; + } + + return sock; +} + +bool wxSocketServer::WaitForAccept(long seconds, long milliseconds) +{ + return _Wait(seconds, milliseconds, GSOCK_CONNECTION_FLAG); +} + +// ========================================================================== +// wxSocketClient +// ========================================================================== + +// -------------------------------------------------------------------------- +// Ctor and dtor +// -------------------------------------------------------------------------- + +wxSocketClient::wxSocketClient(wxSocketFlags flags) + : wxSocketBase(flags, wxSOCKET_CLIENT) +{ +} + +wxSocketClient::~wxSocketClient() +{ +} + +// -------------------------------------------------------------------------- +// Connect +// -------------------------------------------------------------------------- + +bool wxSocketClient::Connect(wxSockAddress& addr_man, bool wait) +{ + GSocketError err; + + if (m_socket) + { + // Shutdown and destroy the socket + Close(); + GSocket_destroy(m_socket); + } + + m_socket = GSocket_new(); + m_connected = false; + m_establishing = false; + + if (!m_socket) + return false; + + GSocket_SetTimeout(m_socket, m_timeout * 1000); + GSocket_SetCallback(m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG | + GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG, + wx_socket_callback, (char *)this); + + // If wait == false, then the call should be nonblocking. + // When we are finished, we put the socket to blocking mode + // again. + + if (!wait) + GSocket_SetNonBlocking(m_socket, 1); + + GSocket_SetPeer(m_socket, addr_man.GetAddress()); + err = GSocket_Connect(m_socket, GSOCK_STREAMED); + + if (!wait) + GSocket_SetNonBlocking(m_socket, 0); + + if (err != GSOCK_NOERROR) + { + if (err == GSOCK_WOULDBLOCK) + m_establishing = true; + + return false; + } + + m_connected = true; + return true; +} + +bool wxSocketClient::WaitOnConnect(long seconds, long milliseconds) +{ + if (m_connected) // Already connected + return true; + + if (!m_establishing || !m_socket) // No connection in progress + return false; + + return _Wait(seconds, milliseconds, GSOCK_CONNECTION_FLAG | GSOCK_LOST_FLAG); +} + +// ========================================================================== +// wxDatagramSocket +// ========================================================================== + +/* NOTE: experimental stuff - might change */ + +wxDatagramSocket::wxDatagramSocket( wxSockAddress& addr, + wxSocketFlags flags ) + : wxSocketBase( flags, wxSOCKET_DATAGRAM ) +{ +#if 0 + // Create the socket + m_socket = GSocket_new(); + + if (!m_socket) + return; + + // Setup the socket as non connection oriented + GSocket_SetLocal(m_socket, addr.GetAddress()); + if( GSocket_SetNonOriented(m_socket) != GSOCK_NOERROR ) + { + GSocket_destroy(m_socket); + m_socket = NULL; + return; + } + + // Initialize all stuff + m_connected = false; + m_establishing = false; + GSocket_SetTimeout( m_socket, m_timeout ); + GSocket_SetCallback( m_socket, GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG | + GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG, + wx_socket_callback, (char*)this ); +#endif +} + +wxDatagramSocket& wxDatagramSocket::RecvFrom( wxSockAddress& addr, + void* buf, + wxUint32 nBytes ) +{ + Read(buf, nBytes); + GetPeer(addr); + return (*this); +} + +wxDatagramSocket& wxDatagramSocket::SendTo( wxSockAddress& addr, + const void* buf, + wxUint32 nBytes ) +{ + GSocket_SetPeer(m_socket, addr.GetAddress()); + Write(buf, nBytes); + return (*this); +} + +/* + * ------------------------------------------------------------------------- + * GAddress + * ------------------------------------------------------------------------- + */ + +/* CHECK_ADDRESS verifies that the current address family is either + * GSOCK_NOFAMILY or GSOCK_*family*, and if it is GSOCK_NOFAMILY, it + * initalizes it to be a GSOCK_*family*. In other cases, it returns + * an appropiate error code. + * + * CHECK_ADDRESS_RETVAL does the same but returning 'retval' on error. + */ +#define CHECK_ADDRESS(address, family) \ +{ \ + if (address->m_family == GSOCK_NOFAMILY) \ + if (_GAddress_Init_##family(address) != GSOCK_NOERROR) \ + return address->m_error; \ + if (address->m_family != GSOCK_##family) \ + { \ + address->m_error = GSOCK_INVADDR; \ + return GSOCK_INVADDR; \ + } \ +} + +#define CHECK_ADDRESS_RETVAL(address, family, retval) \ +{ \ + if (address->m_family == GSOCK_NOFAMILY) \ + if (_GAddress_Init_##family(address) != GSOCK_NOERROR) \ + return retval; \ + if (address->m_family != GSOCK_##family) \ + { \ + address->m_error = GSOCK_INVADDR; \ + return retval; \ + } \ +} + + +GAddress *GAddress_new(void) +{ + GAddress *address; + + if ((address = (GAddress *) malloc(sizeof(GAddress))) == NULL) + return NULL; + + address->m_family = GSOCK_NOFAMILY; + address->m_addr = NULL; + address->m_len = 0; + + return address; +} + +GAddress *GAddress_copy(GAddress *address) +{ + GAddress *addr2; + + assert(address != NULL); + + if ((addr2 = (GAddress *) malloc(sizeof(GAddress))) == NULL) + return NULL; + + memcpy(addr2, address, sizeof(GAddress)); + + if (address->m_addr && address->m_len > 0) + { + addr2->m_addr = (struct sockaddr *)malloc(addr2->m_len); + if (addr2->m_addr == NULL) + { + free(addr2); + return NULL; + } + + memcpy(addr2->m_addr, address->m_addr, addr2->m_len); + } + + return addr2; +} + +void GAddress_destroy(GAddress *address) +{ + assert( address != NULL ); + + if (address->m_addr) + free(address->m_addr); + + free(address); +} + +void GAddress_SetFamily(GAddress *address, GAddressType type) +{ + assert(address != NULL); + + address->m_family = type; +} + +GAddressType GAddress_GetFamily(GAddress *address) +{ + assert( address != NULL ); + + return address->m_family; +} + +GSocketError _GAddress_translate_from(GAddress *address, + struct sockaddr *addr, int len) +{ + address->m_realfamily = addr->sa_family; + switch (addr->sa_family) + { + case AF_INET: + address->m_family = GSOCK_INET; + break; + + case AF_UNIX: + address->m_family = GSOCK_UNIX; + break; + +#ifdef AF_INET6 + case AF_INET6: + address->m_family = GSOCK_INET6; + break; +#endif + + default: + { + address->m_error = GSOCK_INVOP; + return GSOCK_INVOP; + } + } + + if (address->m_addr) + free(address->m_addr); + + address->m_len = len; + address->m_addr = (struct sockaddr *)malloc(len); + + if (address->m_addr == NULL) + { + address->m_error = GSOCK_MEMERR; + return GSOCK_MEMERR; + } + + memcpy(address->m_addr, addr, len); + + return GSOCK_NOERROR; +} + +GSocketError _GAddress_translate_to(GAddress *address, + struct sockaddr **addr, int *len) +{ + if (!address->m_addr) + { + address->m_error = GSOCK_INVADDR; + return GSOCK_INVADDR; + } + + *len = address->m_len; + *addr = (struct sockaddr *)malloc(address->m_len); + if (*addr == NULL) + { + address->m_error = GSOCK_MEMERR; + return GSOCK_MEMERR; + } + + memcpy(*addr, address->m_addr, address->m_len); + return GSOCK_NOERROR; +} + +/* + * ------------------------------------------------------------------------- + * Internet address family + * ------------------------------------------------------------------------- + */ + +GSocketError _GAddress_Init_INET(GAddress *address) +{ + address->m_len = sizeof(struct sockaddr_in); + address->m_addr = (struct sockaddr *) malloc(address->m_len); + if (address->m_addr == NULL) + { + address->m_error = GSOCK_MEMERR; + return GSOCK_MEMERR; + } + + memset( address->m_addr , 0 , address->m_len ) ; + address->m_family = GSOCK_INET; + address->m_realfamily = PF_INET; + ((struct sockaddr_in *)address->m_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)address->m_addr)->sin_addr.s_addr = INADDR_ANY; + + return GSOCK_NOERROR; +} + +GSocketError GAddress_INET_SetHostName(GAddress *address, const char *hostname) +{ + struct hostent *he; + struct in_addr *addr; + + assert( address != NULL ); + CHECK_ADDRESS( address, INET ); + + addr = &(((struct sockaddr_in *)address->m_addr)->sin_addr); + + // If it is a numeric host name, convert it now +#if defined(HAVE_INET_ATON) + if (inet_aton(hostname, addr) == 0) + { +#elif defined(HAVE_INET_ADDR) + if ( (addr->s_addr = inet_addr(hostname)) == -1 ) + { +#else + // Use gethostbyname by default +#ifndef __WXMAC__ + int val = 1; // VA doesn't like constants in conditional expressions + if (val) +#endif + { +#endif + struct in_addr *array_addr; + + // It is a real name, we solve it + if ((he = gethostbyname(hostname)) == NULL) + { + // Reset to invalid address + addr->s_addr = INADDR_NONE; + address->m_error = GSOCK_NOHOST; + return GSOCK_NOHOST; + } + array_addr = (struct in_addr *) *(he->h_addr_list); + addr->s_addr = array_addr[0].s_addr; + } + + return GSOCK_NOERROR; +} + +GSocketError GAddress_INET_SetBroadcastAddress(GAddress *address) +{ + return GAddress_INET_SetHostAddress(address, INADDR_BROADCAST); +} + +GSocketError GAddress_INET_SetAnyAddress(GAddress *address) +{ + return GAddress_INET_SetHostAddress(address, INADDR_ANY); +} + +GSocketError GAddress_INET_SetHostAddress(GAddress *address, + unsigned long hostaddr) +{ + struct in_addr *addr; + + assert( address != NULL ); + CHECK_ADDRESS( address, INET ); + + addr = &(((struct sockaddr_in *)address->m_addr)->sin_addr); + addr->s_addr = htonl(hostaddr) ; + + return GSOCK_NOERROR; +} + +GSocketError GAddress_INET_SetPortName(GAddress *address, const char *port, + const char *protocol) +{ + struct servent *se; + struct sockaddr_in *addr; + + assert( address != NULL ); + CHECK_ADDRESS( address, INET ); + + if (!port) + { + address->m_error = GSOCK_INVPORT; + return GSOCK_INVPORT; + } + + se = getservbyname(port, protocol); + if (!se) + { + // the cast to int suppresses compiler warnings + // about subscript having the type char + if (isdigit((int)port[0])) + { + int port_int; + + port_int = atoi(port); + addr = (struct sockaddr_in *)address->m_addr; + addr->sin_port = htons(port_int); + return GSOCK_NOERROR; + } + + address->m_error = GSOCK_INVPORT; + return GSOCK_INVPORT; + } + + addr = (struct sockaddr_in *)address->m_addr; + addr->sin_port = se->s_port; + + return GSOCK_NOERROR; +} + +GSocketError GAddress_INET_SetPort(GAddress *address, unsigned short port) +{ + struct sockaddr_in *addr; + + assert( address != NULL ); + CHECK_ADDRESS( address, INET ); + + addr = (struct sockaddr_in *)address->m_addr; + addr->sin_port = htons(port); + + return GSOCK_NOERROR; +} + +GSocketError GAddress_INET_GetHostName(GAddress *address, char *hostname, size_t sbuf) +{ + struct hostent *he; + char *addr_buf; + struct sockaddr_in *addr; + + assert( address != NULL ); + CHECK_ADDRESS( address, INET ); + + addr = (struct sockaddr_in *)address->m_addr; + addr_buf = (char *)&(addr->sin_addr); + + he = gethostbyaddr(addr_buf, sizeof(addr->sin_addr), AF_INET); + if (he == NULL) + { + address->m_error = GSOCK_NOHOST; + return GSOCK_NOHOST; + } + + strncpy(hostname, he->h_name, sbuf); + + return GSOCK_NOERROR; +} + +unsigned long GAddress_INET_GetHostAddress(GAddress *address) +{ + struct sockaddr_in *addr; + + assert( address != NULL ); + CHECK_ADDRESS_RETVAL( address, INET, 0 ); + + addr = (struct sockaddr_in *)address->m_addr; + + return ntohl(addr->sin_addr.s_addr) ; +} + +unsigned short GAddress_INET_GetPort(GAddress *address) +{ + struct sockaddr_in *addr; + + assert( address != NULL ); + CHECK_ADDRESS_RETVAL( address, INET, 0 ); + + addr = (struct sockaddr_in *)address->m_addr; + + return ntohs(addr->sin_port); +} + +/* + * ------------------------------------------------------------------------- + * Unix address family + * ------------------------------------------------------------------------- + */ + +GSocketError _GAddress_Init_UNIX(GAddress *address) +{ + address->m_len = sizeof(struct sockaddr_un); + address->m_addr = (struct sockaddr *)malloc(address->m_len); + if (address->m_addr == NULL) + { + address->m_error = GSOCK_MEMERR; + return GSOCK_MEMERR; + } + + address->m_family = GSOCK_UNIX; + address->m_realfamily = PF_UNIX; + ((struct sockaddr_un *)address->m_addr)->sun_family = AF_UNIX; + ((struct sockaddr_un *)address->m_addr)->sun_path[0] = 0; + + return GSOCK_NOERROR; +} + +#define UNIX_SOCK_PATHLEN (sizeof(addr->sun_path)/sizeof(addr->sun_path[0])) + +GSocketError GAddress_UNIX_SetPath(GAddress *address, const char *path) +{ + struct sockaddr_un *addr; + + assert( address != NULL ); + CHECK_ADDRESS( address, UNIX ); + + addr = ((struct sockaddr_un *)address->m_addr); + strncpy(addr->sun_path, path, UNIX_SOCK_PATHLEN); + addr->sun_path[UNIX_SOCK_PATHLEN - 1] = '\0'; + + return GSOCK_NOERROR; +} + +GSocketError GAddress_UNIX_GetPath(GAddress *address, char *path, size_t sbuf) +{ + struct sockaddr_un *addr; + + assert( address != NULL ); + CHECK_ADDRESS( address, UNIX ); + + addr = (struct sockaddr_un *)address->m_addr; + + strncpy(path, addr->sun_path, sbuf); + + return GSOCK_NOERROR; +} + +/* Address handling */ + +/* GSocket_SetLocal: + * GSocket_GetLocal: + * GSocket_SetPeer: + * GSocket_GetPeer: + * Set or get the local or peer address for this socket. The 'set' + * functions return GSOCK_NOERROR on success, an error code otherwise. + * The 'get' functions return a pointer to a GAddress object on success, + * or NULL otherwise, in which case they set the error code of the + * corresponding GSocket. + * + * Error codes: + * GSOCK_INVSOCK - the socket is not valid. + * GSOCK_INVADDR - the address is not valid. + */ + +GSocketError GSocket_SetLocal(GSocket *socket, GAddress *address) +{ + assert( socket != NULL ); + + // the socket must be initialized, or it must be a server + if ((socket->m_fd != INVALID_SOCKET && !socket->m_server)) + { + socket->m_error = GSOCK_INVSOCK; + return GSOCK_INVSOCK; + } + + // check address + if (address == NULL || address->m_family == GSOCK_NOFAMILY) + { + socket->m_error = GSOCK_INVADDR; + return GSOCK_INVADDR; + } + + if (socket->m_local) + GAddress_destroy(socket->m_local); + + socket->m_local = GAddress_copy(address); + + return GSOCK_NOERROR; +} + +GSocketError GSocket_SetPeer(GSocket *socket, GAddress *address) +{ + assert(socket != NULL); + + // check address + if (address == NULL || address->m_family == GSOCK_NOFAMILY) + { + socket->m_error = GSOCK_INVADDR; + return GSOCK_INVADDR; + } + + if (socket->m_peer) + GAddress_destroy(socket->m_peer); + + socket->m_peer = GAddress_copy(address); + + return GSOCK_NOERROR; +} + +GAddress *GSocket_GetLocal(GSocket *socket) +{ + GAddress *address; + struct sockaddr addr; + socklen_t size = sizeof(addr); + GSocketError err; + + assert( socket != NULL ); + + // try to get it from the m_local var first + if (socket->m_local) + return GAddress_copy(socket->m_local); + + // else, if the socket is initialized, try getsockname + if (socket->m_fd == INVALID_SOCKET) + { + socket->m_error = GSOCK_INVSOCK; + return NULL; + } + + if (getsockname(socket->m_fd, &addr, (socklen_t *) &size) < 0) + { + socket->m_error = GSOCK_IOERR; + return NULL; + } + + // got a valid address from getsockname, create a GAddress object + address = GAddress_new(); + if (address == NULL) + { + socket->m_error = GSOCK_MEMERR; + return NULL; + } + + err = _GAddress_translate_from(address, &addr, size); + if (err != GSOCK_NOERROR) + { + GAddress_destroy(address); + socket->m_error = err; + return NULL; + } + + return address; +} + +GAddress *GSocket_GetPeer(GSocket *socket) +{ + assert(socket != NULL); + + // try to get it from the m_peer var + if (socket->m_peer) + return GAddress_copy(socket->m_peer); + + return NULL; +} + + +GSocket *GSocket_new(void) +{ + GSocket *socket; + socket = (GSocket *)malloc(sizeof(GSocket)); + + if (socket == NULL) + return NULL; + + socket->m_fd = INVALID_SOCKET; + + for (int i=0;im_cbacks[i] = NULL; + } + + socket->m_detected = 0; + + socket->m_local = NULL; + socket->m_peer = NULL; + socket->m_error = GSOCK_NOERROR; + + socket->m_non_blocking = false ; + socket->m_stream = true; +// socket->m_oriented = true; + socket->m_server = false; + socket->m_establishing = false; + socket->m_timeout = 10 * 60 * 1000; + // 10 minutes * 60 sec * 1000 millisec + + socket->m_cfSocket = NULL ; + socket->m_runLoopSource = NULL ; + socket->m_readStream = NULL; + socket->m_writeStream = NULL; + + return socket ; +} + +void GSocket_close(GSocket *socket) +{ + if ( socket->m_cfSocket != NULL ) + { + if ( socket->m_readStream ) + { + CFReadStreamClose(socket->m_readStream); + CFRelease( socket->m_readStream ) ; + socket->m_readStream = NULL ; + } + + if ( socket->m_writeStream ) + { + CFWriteStreamClose(socket->m_writeStream); + CFRelease( socket->m_writeStream ) ; + socket->m_writeStream = NULL ; + } + + CFSocketInvalidate( socket->m_cfSocket ) ; + CFRelease( socket->m_cfSocket ) ; + socket->m_cfSocket = NULL ; + socket->m_fd = INVALID_SOCKET ; + } +} + +void GSocket_Shutdown(GSocket *socket) +{ + GSocket_close( socket ); + + // Disable GUI callbacks + for (int evt = 0; evt < GSOCK_MAX_EVENT; evt++) + socket->m_cbacks[evt] = NULL; + + socket->m_detected = GSOCK_LOST_FLAG; +} + +void GSocket_destroy(GSocket *socket) +{ + assert( socket != NULL ); + + // Check that the socket is really shut down + if (socket->m_fd != INVALID_SOCKET) + GSocket_Shutdown(socket); + + // Destroy private addresses + if (socket->m_local) + GAddress_destroy(socket->m_local); + + if (socket->m_peer) + GAddress_destroy(socket->m_peer); + + // Destroy the socket itself + free(socket); +} + +GSocketError GSocket_Connect(GSocket *socket, GSocketStream stream) +{ + assert( socket != NULL ); + + if (socket->m_fd != INVALID_SOCKET) + { + socket->m_error = GSOCK_INVSOCK; + return GSOCK_INVSOCK; + } + + if (!socket->m_peer) + { + socket->m_error = GSOCK_INVADDR; + return GSOCK_INVADDR; + } + + // Streamed or dgram socket? + socket->m_stream = (stream == GSOCK_STREAMED); + socket->m_oriented = true; + socket->m_server = false; + socket->m_establishing = false; + + GSocketError returnErr = GSOCK_NOERROR ; + CFSocketError err ; + + CFAllocatorRef alloc = kCFAllocatorDefault ; + CFSocketContext ctx ; + memset( &ctx , 0 , sizeof( ctx ) ) ; + ctx.info = socket ; + socket->m_cfSocket = CFSocketCreate( alloc , socket->m_peer->m_realfamily , + stream == GSOCK_STREAMED ? SOCK_STREAM : SOCK_DGRAM , 0 , + kCFSocketReadCallBack | kCFSocketWriteCallBack | kCFSocketConnectCallBack , wxMacCFSocketCallback , &ctx ) ; + _GSocket_Enable(socket, GSOCK_CONNECTION); + + socket->m_fd = CFSocketGetNative( socket->m_cfSocket ) ; + + CFStreamCreatePairWithSocket ( alloc , socket->m_fd , &socket->m_readStream , &socket->m_writeStream ); + if ((socket->m_readStream == NULL) || (socket->m_writeStream == NULL)) + { + GSocket_close(socket); + socket->m_error = GSOCK_IOERR; + return GSOCK_IOERR; + } + + if ( !CFReadStreamOpen( socket->m_readStream ) || !CFWriteStreamOpen( socket->m_writeStream ) ) + { + GSocket_close(socket); + socket->m_error = GSOCK_IOERR; + return GSOCK_IOERR; + } + + CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(alloc , socket->m_cfSocket , 0); + CFRunLoopAddSource(CFRunLoopGetCurrent() , rls, kCFRunLoopCommonModes); + CFRelease(rls); + + CFDataRef address = CFDataCreateWithBytesNoCopy(alloc, (const UInt8*) socket->m_peer->m_addr, socket->m_peer->m_len , kCFAllocatorNull); + if ( !address ) + return GSOCK_MEMERR ; + + err = CFSocketConnectToAddress( socket->m_cfSocket , address, socket->m_non_blocking ? -1 : socket->m_timeout / 1000 ) ; + CFRelease(address); + + if (err != kCFSocketSuccess) + { + if ( err == kCFSocketTimeout ) + { + GSocket_close(socket); + socket->m_error = GSOCK_TIMEDOUT ; + return GSOCK_TIMEDOUT ; + } + + // we don't know whether a connect in progress will be issued like this + if ( err != kCFSocketTimeout && socket->m_non_blocking ) + { + socket->m_establishing = true; + socket->m_error = GSOCK_WOULDBLOCK; + return GSOCK_WOULDBLOCK; + } + + GSocket_close(socket); + socket->m_error = GSOCK_IOERR; + return GSOCK_IOERR; + } + + return GSOCK_NOERROR; +} + +/* Flags */ + +/* GSocket_SetNonBlocking: + * Sets the socket to non-blocking mode. + * All IO calls will return immediately. + */ +void GSocket_SetNonBlocking(GSocket *socket, int non_block) +{ + assert( socket != NULL ); + +// GSocket_Debug( ("GSocket_SetNonBlocking: %d\n", (int)non_block) ); + + socket->m_non_blocking = non_block; +} + +/* + * GSocket_SetTimeout: + * Sets the timeout for blocking calls. Time is expressed in + * milliseconds. + */ +void GSocket_SetTimeout(GSocket *socket, unsigned long millisec) +{ + assert( socket != NULL ); + + socket->m_timeout = millisec; +} + +/* GSocket_GetError: + * Returns the last error which occurred for this socket. Note that successful + * operations do not clear this back to GSOCK_NOERROR, so use it only + * after an error. + */ +GSocketError GSocket_GetError(GSocket *socket) +{ + assert( socket != NULL ); + + return socket->m_error; +} + +/* Callbacks */ + +/* GSOCK_INPUT: + * There is data to be read in the input buffer. If, after a read + * operation, there is still data available, the callback function will + * be called again. + * GSOCK_OUTPUT: + * The socket is available for writing. That is, the next write call + * won't block. This event is generated only once, when the connection is + * first established, and then only if a call failed with GSOCK_WOULDBLOCK, + * when the output buffer empties again. This means that the app should + * assume that it can write since the first OUTPUT event, and no more + * OUTPUT events will be generated unless an error occurs. + * GSOCK_CONNECTION: + * Connection successfully established, for client sockets, or incoming + * client connection, for server sockets. Wait for this event (also watch + * out for GSOCK_LOST) after you issue a nonblocking GSocket_Connect() call. + * GSOCK_LOST: + * The connection is lost (or a connection request failed); this could + * be due to a failure, or due to the peer closing it gracefully. + */ + +/* GSocket_SetCallback: + * Enables the callbacks specified by 'flags'. Note that 'flags' + * may be a combination of flags OR'ed toghether, so the same + * callback function can be made to accept different events. + * The callback function must have the following prototype: + * + * void function(GSocket *socket, GSocketEvent event, char *cdata) + */ +void GSocket_SetCallback(GSocket *socket, GSocketEventFlags flags, + GSocketCallback callback, char *cdata) +{ + int count; + + assert( socket != NULL ); + + for (count = 0; count < GSOCK_MAX_EVENT; count++) + { + if ((flags & (1 << count)) != 0) + { + socket->m_cbacks[count] = callback; + socket->m_data[count] = cdata; + } + } +} + +/* GSocket_UnsetCallback: + * Disables all callbacks specified by 'flags', which may be a + * combination of flags OR'ed toghether. + */ +void GSocket_UnsetCallback(GSocket *socket, GSocketEventFlags flags) +{ + int count; + + assert(socket != NULL); + + for (count = 0; count < GSOCK_MAX_EVENT; count++) + { + if ((flags & (1 << count)) != 0) + { + socket->m_cbacks[count] = NULL; + socket->m_data[count] = NULL; + } + } +} + + +#define CALL_CALLBACK(socket, event) { \ + _GSocket_Disable(socket, event); \ + if (socket->m_cbacks[event]) \ + socket->m_cbacks[event](socket, event, socket->m_data[event]); \ +} + +void _GSocket_Install_Callback(GSocket *socket, GSocketEvent event) +{ + int c; + switch (event) + { + case GSOCK_CONNECTION: + if (socket->m_server) + c = kCFSocketReadCallBack; + else + c = kCFSocketConnectCallBack; + break; + + case GSOCK_LOST: + case GSOCK_INPUT: + c = kCFSocketReadCallBack; + break; + + case GSOCK_OUTPUT: + c = kCFSocketWriteCallBack; + break; + + default: + c = 0; + } + + CFSocketEnableCallBacks(socket->m_cfSocket, c); +} + +void _GSocket_Uninstall_Callback(GSocket *socket, GSocketEvent event) +{ + int c; + switch (event) + { + case GSOCK_CONNECTION: + if (socket->m_server) + c = kCFSocketReadCallBack; + else + c = kCFSocketConnectCallBack; + break; + + case GSOCK_LOST: + case GSOCK_INPUT: + c = kCFSocketReadCallBack; + break; + + case GSOCK_OUTPUT: + c = kCFSocketWriteCallBack; + break; + + default: + c = 0; + break; + } + + CFSocketDisableCallBacks(socket->m_cfSocket, c); +} + +void _GSocket_Enable(GSocket *socket, GSocketEvent event) +{ + socket->m_detected &= ~(1 << event); + _GSocket_Install_Callback(socket, event); +} + +void _GSocket_Disable(GSocket *socket, GSocketEvent event) +{ + socket->m_detected |= (1 << event); + _GSocket_Uninstall_Callback(socket, event); +} + +void wxMacCFSocketCallback(CFSocketRef s, CFSocketCallBackType callbackType, + CFDataRef address, const void* data, void* info) +{ + GSocket* socket = (GSocket*)info; + + switch (callbackType) + { + case kCFSocketConnectCallBack: + if ( data ) + { + SInt32 error = *((SInt32*)data) ; + CALL_CALLBACK( socket , GSOCK_LOST ) ; + GSocket_Shutdown(socket); + } + else + { + CALL_CALLBACK( socket , GSOCK_CONNECTION ) ; + } + break; + + case kCFSocketReadCallBack: + CALL_CALLBACK( socket , GSOCK_INPUT ) ; + break; + + case kCFSocketWriteCallBack: + CALL_CALLBACK( socket , GSOCK_OUTPUT ) ; + break; + + default: + break; // We shouldn't get here. + } +} + +int GSocket_Read(GSocket *socket, char *buffer, int size) +{ + int ret = 0 ; + + assert(socket != NULL); + // if ( !CFReadStreamHasBytesAvailable() ) + ret = CFReadStreamRead( socket->m_readStream , (UInt8*) buffer , size ) ; + + return ret; +} + +int GSocket_Write(GSocket *socket, const char *buffer, int size) +{ + int ret; + + assert(socket != NULL); + ret = CFWriteStreamWrite( socket->m_writeStream , (UInt8*) buffer , size ) ; + + return ret; +} + +GSocketEventFlags GSocket_Select(GSocket *socket, GSocketEventFlags flags) +{ + assert( socket != NULL ); + + return flags & socket->m_detected; +} + +// ========================================================================== +// wxSocketModule +// ========================================================================== + +class wxSocketModule : public wxModule +{ +public: + virtual bool OnInit() + { + // wxSocketBase will call GSocket_Init() itself when/if needed + return true; + } + + virtual void OnExit() + { + if ( wxSocketBase::IsInitialized() ) + wxSocketBase::Shutdown(); + } + +private: + DECLARE_DYNAMIC_CLASS(wxSocketModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxSocketModule, wxModule) + +#endif + // wxUSE_SOCKETS diff --git a/src/osx/carbon/checkbox.cpp b/src/osx/carbon/checkbox.cpp new file mode 100644 index 0000000000..982869b0aa --- /dev/null +++ b/src/osx/carbon/checkbox.cpp @@ -0,0 +1,186 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/checkbox.cpp +// Purpose: wxCheckBox +// Author: Stefan Csomor +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_CHECKBOX + +#include "wx/checkbox.h" +#include "wx/mac/uma.h" + + +IMPLEMENT_DYNAMIC_CLASS(wxCheckBox, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxBitmapCheckBox, wxCheckBox) + + +// Single check box item +bool wxCheckBox::Create(wxWindow *parent, + wxWindowID id, + const wxString& label, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + m_macIsUserPane = false ; + + if ( !wxCheckBoxBase::Create(parent, id, pos, size, style, validator, name) ) + return false; + + m_labelOrig = m_label = label ; + + SInt32 maxValue = 1 /* kControlCheckboxCheckedValue */; + if (style & wxCHK_3STATE) + maxValue = 2 /* kControlCheckboxMixedValue */; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ; + m_peer = new wxMacControl( this ) ; + verify_noerr( CreateCheckBoxControl(MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds , + CFSTR("") , 0 , false , m_peer->GetControlRefAddr() ) ); + + m_peer->SetMaximum( maxValue ) ; + + MacPostControlCreate(pos, size) ; + + return true; +} + +void wxCheckBox::SetValue(bool val) +{ + if (val) + Set3StateValue(wxCHK_CHECKED); + else + Set3StateValue(wxCHK_UNCHECKED); +} + +bool wxCheckBox::GetValue() const +{ + return (DoGet3StateValue() != 0); +} + +void wxCheckBox::Command(wxCommandEvent & event) +{ + int state = event.GetInt(); + + wxCHECK_RET( (state == wxCHK_UNCHECKED) || (state == wxCHK_CHECKED) + || (state == wxCHK_UNDETERMINED), + wxT("event.GetInt() returned an invalid checkbox state") ); + + Set3StateValue((wxCheckBoxState)state); + + ProcessCommand(event); +} + +wxCheckBoxState wxCheckBox::DoGet3StateValue() const +{ + return (wxCheckBoxState)m_peer->GetValue() ; +} + +void wxCheckBox::DoSet3StateValue(wxCheckBoxState val) +{ + m_peer->SetValue( val ) ; +} + +wxInt32 wxCheckBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCheckBoxState origState, newState; + + newState = origState = Get3StateValue(); + + switch (origState) + { + case wxCHK_UNCHECKED: + newState = wxCHK_CHECKED; + break; + + case wxCHK_CHECKED: + // If the style flag to allow the user setting the undetermined state is set, + // then set the state to undetermined; otherwise set state to unchecked. + newState = Is3rdStateAllowedForUser() ? wxCHK_UNDETERMINED : wxCHK_UNCHECKED; + break; + + case wxCHK_UNDETERMINED: + newState = wxCHK_UNCHECKED; + break; + + default: + break; + } + + if (newState != origState) + { + Set3StateValue( newState ); + + wxCommandEvent event( wxEVT_COMMAND_CHECKBOX_CLICKED, m_windowId ); + event.SetInt( newState ); + event.SetEventObject( this ); + ProcessCommand( event ); + } + + return noErr; +} + +// Bitmap checkbox +bool wxBitmapCheckBox::Create(wxWindow *parent, + wxWindowID id, + const wxBitmap *WXUNUSED(label), + const wxPoint& WXUNUSED(pos), + const wxSize& WXUNUSED(size), + long style, + const wxValidator& wxVALIDATOR_PARAM(validator), + const wxString& name) +{ + SetName(name); +#if wxUSE_VALIDATORS + SetValidator(validator); +#endif + m_windowStyle = style; + + if (parent) + parent->AddChild(this); + + if ( id == -1 ) + m_windowId = NewControlId(); + else + m_windowId = id; + + // TODO: Create the bitmap checkbox + + return false; +} + +void wxBitmapCheckBox::SetLabel(const wxBitmap *WXUNUSED(bitmap)) +{ + // TODO + wxFAIL_MSG(wxT("wxBitmapCheckBox::SetLabel() not yet implemented")); +} + +void wxBitmapCheckBox::SetSize(int x, int y, int width, int height, int sizeFlags) +{ + wxControl::SetSize( x , y , width , height , sizeFlags ) ; +} + +void wxBitmapCheckBox::SetValue(bool WXUNUSED(val)) +{ + // TODO + wxFAIL_MSG(wxT("wxBitmapCheckBox::SetValue() not yet implemented")); +} + +bool wxBitmapCheckBox::GetValue() const +{ + // TODO + wxFAIL_MSG(wxT("wxBitmapCheckBox::GetValue() not yet implemented")); + + return false; +} + +#endif diff --git a/src/osx/carbon/checklst.cpp b/src/osx/carbon/checklst.cpp new file mode 100644 index 0000000000..5f0fecb5e2 --- /dev/null +++ b/src/osx/carbon/checklst.cpp @@ -0,0 +1,273 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/checklst.cpp +// Purpose: implementation of wxCheckListBox class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// +// +// new DataBrowser-based version + + +#include "wx/wxprec.h" + +#if wxUSE_CHECKLISTBOX + +#include "wx/checklst.h" + +#ifndef WX_PRECOMP + #include "wx/arrstr.h" +#endif + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxCheckListBox, wxListBox) + +BEGIN_EVENT_TABLE(wxCheckListBox, wxListBox) +END_EVENT_TABLE() + +class wxMacDataBrowserCheckListControl : public wxMacDataBrowserListControl , public wxMacCheckListControl +{ +public: + wxMacDataBrowserCheckListControl( wxListBox *peer, const wxPoint& pos, const wxSize& size, long style ); + wxMacDataBrowserCheckListControl() {} + virtual ~wxMacDataBrowserCheckListControl(); + + virtual wxMacDataItem* CreateItem(); + + virtual bool MacIsChecked(unsigned int n) const; + virtual void MacCheck(unsigned int n, bool bCheck = true); + DECLARE_DYNAMIC_CLASS_NO_COPY(wxMacDataBrowserCheckListControl) +}; + +IMPLEMENT_DYNAMIC_CLASS( wxMacDataBrowserCheckListControl , wxMacDataBrowserListControl ) + +void wxCheckListBox::Init() +{ +} + +bool wxCheckListBox::Create( + wxWindow *parent, + wxWindowID id, + const wxPoint &pos, + const wxSize &size, + const wxArrayString& choices, + long style, + const wxValidator& validator, + const wxString &name ) +{ + wxCArrayString chs( choices ); + + return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(), style, validator, name ); +} + +bool wxCheckListBox::Create( + wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + int n, + const wxString choices[], + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_macIsUserPane = false; + + wxASSERT_MSG( !(style & wxLB_MULTIPLE) || !(style & wxLB_EXTENDED), + wxT("only one of listbox selection modes can be specified") ); + + if ( !wxListBoxBase::Create( parent, id, pos, size, style & ~(wxHSCROLL | wxVSCROLL), validator, name ) ) + return false; + + // this will be increased by our Append command + wxMacDataBrowserCheckListControl* control = new wxMacDataBrowserCheckListControl( this, pos, size, style ); + // TODO CHECK control->SetClientDataType( m_clientDataItemsType ); + m_peer = control; + + MacPostControlCreate(pos,size); + + InsertItems( n , choices , 0 ); + + // Needed because it is a wxControlWithItems + SetInitialSize( size ); + + return true; +} + +// ---------------------------------------------------------------------------- +// wxCheckListBox functions +// ---------------------------------------------------------------------------- + +bool wxCheckListBox::IsChecked(unsigned int item) const +{ + wxCHECK_MSG( IsValid(item), false, + wxT("invalid index in wxCheckListBox::IsChecked") ); + + return GetPeer()->MacIsChecked( item ); +} + +void wxCheckListBox::Check(unsigned int item, bool check) +{ + wxCHECK_RET( IsValid(item), + wxT("invalid index in wxCheckListBox::Check") ); + + bool isChecked = GetPeer()->MacIsChecked( item ); + if ( check != isChecked ) + { + GetPeer()->MacCheck( item , check ); + } +} + +wxMacCheckListControl* wxCheckListBox::GetPeer() const +{ + wxMacDataBrowserCheckListControl *lb = wxDynamicCast(m_peer,wxMacDataBrowserCheckListControl); + return lb ? wx_static_cast(wxMacCheckListControl*,lb) : 0 ; +} + +const short kCheckboxColumnId = 1026; + +wxMacDataBrowserCheckListControl::wxMacDataBrowserCheckListControl( wxListBox *peer, const wxPoint& pos, const wxSize& size, long style) + : wxMacDataBrowserListControl( peer, pos, size, style ) +{ + OSStatus err = noErr; + + DataBrowserListViewColumnDesc columnDesc; + columnDesc.headerBtnDesc.titleOffset = 0; + columnDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc; + + columnDesc.headerBtnDesc.btnFontStyle.flags = + kControlUseFontMask | kControlUseJustMask; + + columnDesc.headerBtnDesc.btnContentInfo.contentType = kControlNoContent; + columnDesc.headerBtnDesc.btnFontStyle.just = teFlushDefault; + columnDesc.headerBtnDesc.btnFontStyle.font = kControlFontViewSystemFont; + columnDesc.headerBtnDesc.btnFontStyle.style = normal; + columnDesc.headerBtnDesc.titleString = NULL; + + columnDesc.headerBtnDesc.minimumWidth = 30; + columnDesc.headerBtnDesc.maximumWidth = 30; + + columnDesc.propertyDesc.propertyID = kCheckboxColumnId; + columnDesc.propertyDesc.propertyType = kDataBrowserCheckboxType; + columnDesc.propertyDesc.propertyFlags = + kDataBrowserPropertyIsMutable + | kDataBrowserTableViewSelectionColumn + | kDataBrowserDefaultPropertyFlags; + + err = AddColumn( &columnDesc, 0 ); + verify_noerr( err ); +} + +wxMacDataBrowserCheckListControl::~wxMacDataBrowserCheckListControl() +{ + +} + +class wxMacCheckListBoxItem : public wxMacListBoxItem +{ +public : + wxMacCheckListBoxItem() + { + m_isChecked = false; + } + + virtual ~wxMacCheckListBoxItem() + { + } + + virtual OSStatus GetSetData( wxMacDataItemBrowserControl *owner , + DataBrowserPropertyID property, + DataBrowserItemDataRef itemData, + bool changeValue ) + { + OSStatus err = errDataBrowserPropertyNotSupported; + + wxCheckListBox *checklist = wxDynamicCast( owner->GetPeer() , wxCheckListBox ); + wxCHECK_MSG( checklist != NULL , errDataBrowserPropertyNotSupported , wxT("wxCheckListBox expected")); + + if ( !changeValue ) + { + switch (property) + { + case kCheckboxColumnId: + verify_noerr(SetDataBrowserItemDataButtonValue( itemData, m_isChecked ? kThemeButtonOn : kThemeButtonOff )); + err = noErr; + break; + + case kDataBrowserItemIsEditableProperty: + verify_noerr(SetDataBrowserItemDataBooleanValue( itemData, true )); + err = noErr; + break; + + default: + break; + } + } + else + { + switch (property) + { + case kCheckboxColumnId: + { + // we have to change this behind the back, since Check() would be triggering another update round + bool newVal = !m_isChecked; + verify_noerr(SetDataBrowserItemDataButtonValue( itemData, newVal ? kThemeButtonOn : kThemeButtonOff )); + m_isChecked = newVal; + err = noErr; + + wxCommandEvent event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, checklist->GetId() ); + event.SetInt( owner->GetLineFromItem( this ) ); + event.SetEventObject( checklist ); + checklist->HandleWindowEvent( event ); + } + break; + + default: + break; + } + } + + if ( err == errDataBrowserPropertyNotSupported ) + err = wxMacListBoxItem::GetSetData( owner , property, itemData , changeValue); + + return err; + } + + void Check( bool check ) + { + m_isChecked = check; + } + bool IsChecked() const + { + return m_isChecked; + } + +protected : + bool m_isChecked; +}; + +wxMacDataItem* wxMacDataBrowserCheckListControl::CreateItem() +{ + return new wxMacCheckListBoxItem(); +} + +void wxMacDataBrowserCheckListControl::MacCheck( unsigned int n, bool bCheck) +{ + wxMacCheckListBoxItem* item = wx_static_cast(wxMacCheckListBoxItem*, GetItemFromLine( n) ); + item->Check( bCheck); + UpdateItem(wxMacDataBrowserRootContainer, item , kCheckboxColumnId); +} + +bool wxMacDataBrowserCheckListControl::MacIsChecked( unsigned int n) const +{ + wxMacCheckListBoxItem * item = wx_static_cast( wxMacCheckListBoxItem*, GetItemFromLine( n ) ); + return item->IsChecked(); +} + + + +#endif // wxUSE_CHECKLISTBOX diff --git a/src/osx/carbon/choice.cpp b/src/osx/carbon/choice.cpp new file mode 100644 index 0000000000..c395a39d19 --- /dev/null +++ b/src/osx/carbon/choice.cpp @@ -0,0 +1,308 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/choice.cpp +// Purpose: wxChoice +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_CHOICE + +#include "wx/choice.h" + +#ifndef WX_PRECOMP + #include "wx/menu.h" + #include "wx/dcclient.h" +#endif + +#include "wx/mac/uma.h" + +extern MenuHandle NewUniqueMenu() ; + +IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControlWithItems) + + +wxChoice::~wxChoice() +{ + if ( HasClientObjectData() ) + { + unsigned int i, max = GetCount(); + + for ( i = 0; i < max; ++i ) + delete GetClientObject( i ); + } + + // DeleteMenu( m_macPopUpMenuId ) ; + // DisposeMenu( m_macPopUpMenuHandle ) ; +} + +bool wxChoice::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + const wxArrayString& choices, + long style, + const wxValidator& validator, + const wxString& name ) +{ + if ( !Create( parent, id, pos, size, 0, NULL, style, validator, name ) ) + return false; + + Append( choices ); + + if ( !choices.empty() ) + SetSelection( 0 ); + + SetInitialSize( size ); + + return true; +} + +bool wxChoice::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + int n, + const wxString choices[], + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_macIsUserPane = false; + + if ( !wxChoiceBase::Create( parent, id, pos, size, style, validator, name ) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ); + + m_peer = new wxMacControl( this ) ; + OSStatus err = CreatePopupButtonControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()) , &bounds , CFSTR("") , + -12345 , false /* no variable width */ , 0 , 0 , 0 , m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + m_macPopUpMenuHandle = NewUniqueMenu() ; + m_peer->SetData( kControlNoPart , kControlPopupButtonMenuHandleTag , (MenuHandle) m_macPopUpMenuHandle ) ; + m_peer->SetValueAndRange( n > 0 ? 1 : 0 , 0 , 0 ); + MacPostControlCreate( pos, size ); + +#if !wxUSE_STL + if ( style & wxCB_SORT ) + // autosort + m_strings = wxArrayString( 1 ); +#endif + + Append(n, choices); + + // Set the first item as being selected + if (n > 0) + SetSelection( 0 ); + + // Needed because it is a wxControlWithItems + SetInitialSize( size ); + + return true; +} + +// ---------------------------------------------------------------------------- +// adding/deleting items to/from the list +// ---------------------------------------------------------------------------- + +int wxChoice::DoInsertItems(const wxArrayStringsAdapter & items, + unsigned int pos, + void **clientData, wxClientDataType type) +{ + const unsigned int numItems = items.GetCount(); + for( unsigned int i = 0; i < numItems; ++i, ++pos ) + { + unsigned int idx; + +#if wxUSE_STL + if ( IsSorted() ) + { + wxArrayString::iterator + insertPoint = std::lower_bound( m_strings.begin(), m_strings.end(), items[i] ); + idx = insertPoint - m_strings.begin(); + m_strings.insert( insertPoint, items[i] ); + } + else +#endif // wxUSE_STL + { + idx = pos; + m_strings.Insert( items[i], idx ); + } + + UMAInsertMenuItem(MAC_WXHMENU( m_macPopUpMenuHandle ), + items[i], + GetFont().GetEncoding(), + idx); + m_datas.Insert( NULL, idx ); + AssignNewItemClientData(idx, clientData, i, type); + } + + m_peer->SetMaximum( GetCount() ); + + return pos - 1; +} + +void wxChoice::DoDeleteOneItem(unsigned int n) +{ + wxCHECK_RET( IsValid(n) , wxT("wxChoice::Delete: invalid index") ); + + if ( HasClientObjectData() ) + delete GetClientObject( n ); + + ::DeleteMenuItem( MAC_WXHMENU(m_macPopUpMenuHandle) , n + 1 ) ; + m_strings.RemoveAt( n ) ; + m_datas.RemoveAt( n ) ; + m_peer->SetMaximum( GetCount() ) ; +} + +void wxChoice::DoClear() +{ + for ( unsigned int i = 0 ; i < GetCount() ; i++ ) + { + ::DeleteMenuItem( MAC_WXHMENU(m_macPopUpMenuHandle) , 1 ) ; + } + + m_strings.Empty() ; + m_datas.Empty() ; + m_peer->SetMaximum( 0 ) ; +} + +// ---------------------------------------------------------------------------- +// selection +// ---------------------------------------------------------------------------- +int wxChoice::GetSelection() const +{ + return m_peer->GetValue() - 1 ; +} + +void wxChoice::SetSelection( int n ) +{ + m_peer->SetValue( n + 1 ) ; +} + +// ---------------------------------------------------------------------------- +// string list functions +// ---------------------------------------------------------------------------- + +unsigned int wxChoice::GetCount() const +{ + return m_strings.GetCount() ; +} + +int wxChoice::FindString( const wxString& s, bool bCase ) const +{ +#if !wxUSE_STL + // Avoid assert for non-default args passed to sorted array Index + if ( IsSorted() ) + bCase = true; +#endif + + return m_strings.Index( s , bCase ) ; +} + +void wxChoice::SetString(unsigned int n, const wxString& s) +{ + wxCHECK_RET( IsValid(n), wxT("wxChoice::SetString(): invalid index") ); + + m_strings[n] = s ; + + // apple menu pos is 1-based + UMASetMenuItemText( MAC_WXHMENU(m_macPopUpMenuHandle) , n + 1 , s , wxFont::GetDefaultEncoding() ) ; +} + +wxString wxChoice::GetString(unsigned int n) const +{ + wxCHECK_MSG( IsValid(n), wxEmptyString, wxT("wxChoice::GetString(): invalid index") ); + + return m_strings[n] ; +} + +// ---------------------------------------------------------------------------- +// client data +// ---------------------------------------------------------------------------- +void wxChoice::DoSetItemClientData(unsigned int n, void* clientData) +{ + wxCHECK_RET( IsValid(n), wxT("wxChoice::DoSetItemClientData: invalid index") ); + + m_datas[n] = (char*)clientData ; +} + +void * wxChoice::DoGetItemClientData(unsigned int n) const +{ + wxCHECK_MSG( IsValid(n), NULL, wxT("wxChoice::DoGetClientData: invalid index") ); + + return (void *)m_datas[n]; +} + +wxInt32 wxChoice::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCommandEvent event( wxEVT_COMMAND_CHOICE_SELECTED, m_windowId ); + + // actually n should be made sure by the os to be a valid selection, but ... + int n = GetSelection(); + if ( n > -1 ) + { + event.SetInt( n ); + event.SetString( GetStringSelection() ); + event.SetEventObject( this ); + + if ( HasClientObjectData() ) + event.SetClientObject( GetClientObject( n ) ); + else if ( HasClientUntypedData() ) + event.SetClientData( GetClientData( n ) ); + + ProcessCommand( event ); + } + + return noErr ; +} + +wxSize wxChoice::DoGetBestSize() const +{ + int lbWidth = GetCount() > 0 ? 20 : 100; // some defaults + int lbHeight = 20; + int wLine; + + SInt32 metric ; + + GetThemeMetric( kThemeMetricPopupButtonHeight , &metric ); + lbHeight = metric ; + + { + wxClientDC dc(const_cast(this)); + + // Find the widest line + for(unsigned int i = 0; i < GetCount(); i++) + { + wxString str(GetString(i)); + + wxCoord width, height ; + dc.GetTextExtent( str , &width, &height); + wLine = width ; + + lbWidth = wxMax( lbWidth, wLine ) ; + } + + // Add room for the popup arrow + lbWidth += 2 * lbHeight ; + + wxCoord width, height ; + dc.GetTextExtent( wxT("X"), &width, &height); + int cx = width ; + lbHeight += 4; + + lbWidth += cx ; + } + + return wxSize( lbWidth, lbHeight ); +} + +#endif // wxUSE_CHOICE diff --git a/src/osx/carbon/clipbrd.cpp b/src/osx/carbon/clipbrd.cpp new file mode 100644 index 0000000000..7acd5845f4 --- /dev/null +++ b/src/osx/carbon/clipbrd.cpp @@ -0,0 +1,209 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/clipbrd.cpp +// Purpose: Clipboard functionality +// Author: Stefan Csomor; +// Generalized clipboard implementation by Matthew Flatt +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_CLIPBOARD + +#include "wx/clipbrd.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/frame.h" + #include "wx/bitmap.h" +#endif + +#include "wx/metafile.h" + +#include "wx/mac/uma.h" + +#define wxUSE_DATAOBJ 1 + +#include + +// the trace mask we use with wxLogTrace() - call +// wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here +// (there will be a *lot* of them!) +#define TRACE_CLIPBOARD _T("clipboard") + +IMPLEMENT_DYNAMIC_CLASS(wxClipboard, wxObject) + +wxClipboard::wxClipboard() +{ + m_open = false; + m_data = NULL ; + PasteboardRef clipboard = 0; + OSStatus err = PasteboardCreate( kPasteboardClipboard, &clipboard ); + if (err != noErr) + { + wxLogSysError( wxT("Failed to create the clipboard.") ); + } + m_pasteboard.reset(clipboard); +} + +wxClipboard::~wxClipboard() +{ + m_pasteboard.reset((PasteboardRef)0); + delete m_data; +} + +void wxClipboard::Clear() +{ + if (m_data != NULL) + { + delete m_data; + m_data = NULL; + } + + OSStatus err = PasteboardClear( m_pasteboard ); + if (err != noErr) + { + wxLogSysError( wxT("Failed to empty the clipboard.") ); + } +} + +bool wxClipboard::Flush() +{ + return false; +} + +bool wxClipboard::Open() +{ + wxCHECK_MSG( !m_open, false, wxT("clipboard already open") ); + + m_open = true; + + return true; +} + +bool wxClipboard::IsOpened() const +{ + return m_open; +} + +bool wxClipboard::SetData( wxDataObject *data ) +{ + if ( IsUsingPrimarySelection() ) + return false; + + wxCHECK_MSG( m_open, false, wxT("clipboard not open") ); + wxCHECK_MSG( data, false, wxT("data is invalid") ); + + Clear(); + + // as we can only store one wxDataObject, + // this is the same in this implementation + return AddData( data ); +} + +bool wxClipboard::AddData( wxDataObject *data ) +{ + if ( IsUsingPrimarySelection() ) + return false; + + wxCHECK_MSG( m_open, false, wxT("clipboard not open") ); + wxCHECK_MSG( data, false, wxT("data is invalid") ); + + // we can only store one wxDataObject + Clear(); + + PasteboardSyncFlags syncFlags = PasteboardSynchronize( m_pasteboard ); + wxCHECK_MSG( !(syncFlags&kPasteboardModified), false, wxT("clipboard modified after clear") ); + wxCHECK_MSG( (syncFlags&kPasteboardClientIsOwner), false, wxT("client couldn't own clipboard") ); + + m_data = data; + + data->AddToPasteboard( m_pasteboard, 1 ); + + return true; +} + +void wxClipboard::Close() +{ + wxCHECK_RET( m_open, wxT("clipboard not open") ); + + m_open = false; + + // Get rid of cached object. + // If this is not done, copying data from + // another application will only work once + if (m_data) + { + delete m_data; + m_data = (wxDataObject*) NULL; + } +} + +bool wxClipboard::IsSupported( const wxDataFormat &dataFormat ) +{ + wxLogTrace(TRACE_CLIPBOARD, wxT("Checking if format %s is available"), + dataFormat.GetId().c_str()); + + if ( m_data ) + return m_data->IsSupported( dataFormat ); + return wxDataObject::IsFormatInPasteboard( m_pasteboard, dataFormat ); +} + +bool wxClipboard::GetData( wxDataObject& data ) +{ + if ( IsUsingPrimarySelection() ) + return false; + + wxCHECK_MSG( m_open, false, wxT("clipboard not open") ); + + size_t formatcount = data.GetFormatCount() + 1; + wxDataFormat *array = new wxDataFormat[ formatcount ]; + array[0] = data.GetPreferredFormat(); + data.GetAllFormats( &array[1] ); + + bool transferred = false; + + if ( m_data ) + { + for (size_t i = 0; !transferred && i < formatcount; i++) + { + wxDataFormat format = array[ i ]; + if ( m_data->IsSupported( format ) ) + { + int dataSize = m_data->GetDataSize( format ); + transferred = true; + + if (dataSize == 0) + { + data.SetData( format, 0, 0 ); + } + else + { + char *d = new char[ dataSize ]; + m_data->GetDataHere( format, (void*)d ); + data.SetData( format, dataSize, d ); + delete [] d; + } + } + } + } + + // get formats from wxDataObjects + if ( !transferred ) + { + transferred = data.GetFromPasteboard( m_pasteboard ) ; + } + + delete [] array; + + return transferred; +} + +#endif diff --git a/src/osx/carbon/colordlg.cpp b/src/osx/carbon/colordlg.cpp new file mode 100644 index 0000000000..377276d0c4 --- /dev/null +++ b/src/osx/carbon/colordlg.cpp @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: colordlg.cpp +// Purpose: wxColourDialog class. NOTE: you can use the generic class +// if you wish, instead of implementing this. +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/mac/colordlg.h" +#include "wx/fontdlg.h" + + +#if !USE_NATIVE_FONT_DIALOG_FOR_MACOSX + +IMPLEMENT_DYNAMIC_CLASS(wxColourDialog, wxDialog) + +#include "wx/mac/private.h" + +/* + * wxColourDialog + */ + +wxColourDialog::wxColourDialog() +{ + m_dialogParent = NULL; +} + +wxColourDialog::wxColourDialog(wxWindow *parent, wxColourData *data) +{ + Create(parent, data); +} + +bool wxColourDialog::Create(wxWindow *parent, wxColourData *data) +{ + m_dialogParent = parent; + + if (data) + m_colourData = *data; + return true; +} + +int wxColourDialog::ShowModal() +{ + RGBColor currentColor ; + + m_colourData.m_dataColour.GetRGBColor( currentColor ); + NColorPickerInfo info; + OSStatus err ; + memset(&info, 0, sizeof(info)) ; + // TODO : use parent to determine better position and then kAtSpecifiedOrigin + info.placeWhere = kCenterOnMainScreen ; + info.flags = kColorPickerDialogIsMoveable | kColorPickerDialogIsModal ; + info.theColor.color.rgb.red = currentColor.red ; + info.theColor.color.rgb.green = currentColor.green ; + info.theColor.color.rgb.blue = currentColor.blue ; + err = NPickColor(&info); + if ((err == noErr) && info.newColorChosen) + { + currentColor.red = info.theColor.color.rgb.red ; + currentColor.green = info.theColor.color.rgb.green ; + currentColor.blue = info.theColor.color.rgb.blue ; + m_colourData.m_dataColour = currentColor; + + return wxID_OK; + } + return wxID_CANCEL; +} + +#endif diff --git a/src/osx/carbon/colordlgosx.mm b/src/osx/carbon/colordlgosx.mm new file mode 100644 index 0000000000..ea058c24f7 --- /dev/null +++ b/src/osx/carbon/colordlgosx.mm @@ -0,0 +1,193 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/colordlg.mm +// Purpose: wxColourDialog class. NOTE: you can use the generic class +// if you wish, instead of implementing this. +// Author: Ryan Norton +// Modified by: +// Created: 2004-11-16 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// =========================================================================== +// declarations +// =========================================================================== + +// --------------------------------------------------------------------------- +// headers +// --------------------------------------------------------------------------- + +#include "wx/wxprec.h" + +#include "wx/mac/colordlg.h" +#include "wx/fontdlg.h" + +// ============================================================================ +// implementation +// ============================================================================ + +//Mac OSX 10.2+ only +#if USE_NATIVE_FONT_DIALOG_FOR_MACOSX + +IMPLEMENT_DYNAMIC_CLASS(wxColourDialog, wxDialog) + +// Cocoa headers +#include "wx/cocoa/autorelease.h" +#include "wx/cocoa/string.h" + +#import +#import +#import +#import +#import + +// --------------------------------------------------------------------------- +// wxCPWCDelegate - Window Closed delegate +// --------------------------------------------------------------------------- + +@interface wxCPWCDelegate : NSObject +{ + bool m_bIsClosed; +} + +// Delegate methods +- (id)init; +- (BOOL)windowShouldClose:(id)sender; +- (BOOL)isClosed; +@end // interface wxNSFontPanelDelegate : NSObject + +@implementation wxCPWCDelegate : NSObject + +- (id)init +{ + [super init]; + m_bIsClosed = false; + + return self; +} + +- (BOOL)windowShouldClose:(id)sender +{ + wxUnusedVar(sender); + + m_bIsClosed = true; + + [NSApp abortModal]; + [NSApp stopModal]; + return YES; +} + +- (BOOL)isClosed +{ + return m_bIsClosed; +} + +@end // wxNSFontPanelDelegate + +/* + * wxColourDialog + */ + +wxColourDialog::wxColourDialog() +{ + m_dialogParent = NULL; +} + +wxColourDialog::wxColourDialog(wxWindow *parent, wxColourData *data) +{ + Create(parent, data); +} + +bool wxColourDialog::Create(wxWindow *parent, wxColourData *data) +{ + m_dialogParent = parent; + + if (data) + m_colourData = *data; + + // + // This is the key call - this initializes + // events and window stuff for cocoa for carbon + // applications. + // + // This is also the only call here that is + // 10.2+ specific (the rest is OSX only), + // which, ironically, the carbon font + // panel requires. + // + bool bOK = NSApplicationLoad(); + + //autorelease pool - req'd for carbon + NSAutoreleasePool *thePool; + thePool = [[NSAutoreleasePool alloc] init]; + + if(m_colourData.m_dataColour.Ok()) + [[NSColorPanel sharedColorPanel] setColor: + [NSColor colorWithCalibratedRed:m_colourData.m_dataColour.Red() / 255.0 + green:m_colourData.m_dataColour.Green() / 255.0 + blue:m_colourData.m_dataColour.Blue() / 255.0 + alpha:1.0] + ]; + else + [[NSColorPanel sharedColorPanel] setColor:[NSColor blackColor]]; + + //We're done - free up the pool + [thePool release]; + + return bOK; +} +int wxColourDialog::ShowModal() +{ + //Start the pool. Required for carbon interaction + //(For those curious, the only thing that happens + //if you don't do this is a bunch of error + //messages about leaks on the console, + //with no windows shown or anything). + NSAutoreleasePool *thePool; + thePool = [[NSAutoreleasePool alloc] init]; + + //Get the shared color and font panel + NSColorPanel* theColorPanel = [NSColorPanel sharedColorPanel]; + + //Create and assign the delegates (cocoa event handlers) so + //we can tell if a window has closed/open or not + wxCPWCDelegate* theCPDelegate = [[wxCPWCDelegate alloc] init]; + [theColorPanel setDelegate:theCPDelegate]; + + // + // Start the color panel modal loop + // + NSModalSession session = [NSApp beginModalSessionForWindow:theColorPanel]; + for (;;) + { + [NSApp runModalSession:session]; + + //If the color panel is closed, return the font panel modal loop + if ([theCPDelegate isClosed]) + break; + } + [NSApp endModalSession:session]; + + //free up the memory for the delegates - we don't need them anymore + [theCPDelegate release]; + + //Get the shared color panel along with the chosen color and set the chosen color + NSColor* theColor = [[theColorPanel color] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + + m_colourData.m_dataColour.Set( + (unsigned char) ([theColor redComponent] * 255.0), + (unsigned char) ([theColor greenComponent] * 255.0), + (unsigned char) ([theColor blueComponent] * 255.0) + ); + + //Release the pool, we're done :) + [thePool release]; + + //Return ID_OK - there are no "apply" buttons or the like + //on either the font or color panel + return wxID_OK; +} + +#endif //use native font dialog + diff --git a/src/osx/carbon/colour.cpp b/src/osx/carbon/colour.cpp new file mode 100644 index 0000000000..9eb24718a3 --- /dev/null +++ b/src/osx/carbon/colour.cpp @@ -0,0 +1,147 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/colour.cpp +// Purpose: wxColour class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/colour.h" + +#ifndef WX_PRECOMP + #include "wx/gdicmn.h" +#endif + +#include "wx/mac/private.h" + +IMPLEMENT_DYNAMIC_CLASS(wxColour, wxObject) + +#if wxMAC_USE_QUICKDRAW +wxColour::wxColour(const RGBColor& col) +{ + InitRGBColor(col); +} +#endif + +wxColour::wxColour(CGColorRef col) +{ + InitCGColorRef(col); +} + +#if wxMAC_USE_QUICKDRAW +void wxColour::GetRGBColor( RGBColor *col ) const +{ + col->red = (m_red << 8) + m_red; + col->blue = (m_blue << 8) + m_blue; + col->green = (m_green << 8) + m_green; +} + +wxColour& wxColour::operator=(const RGBColor& col) +{ + InitRGBColor(col); + return *this; +} +#endif + +wxColour& wxColour::operator=(CGColorRef col) +{ + InitCGColorRef(col); + return *this; +} + +wxColour& wxColour::operator=(const wxColour& col) +{ + m_red = col.m_red; + m_green = col.m_green; + m_blue = col.m_blue; + m_alpha = col.m_alpha; + m_cgColour = col.m_cgColour; + return *this; +} + +void wxColour::InitRGBA (ChannelType r, ChannelType g, ChannelType b, ChannelType a) +{ + m_red = r; + m_green = g; + m_blue = b; + m_alpha = a ; + + CGColorRef col = 0 ; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( CGColorCreateGenericRGB ) + col = CGColorCreateGenericRGB( (CGFloat)(r / 255.0), (CGFloat) (g / 255.0), (CGFloat) (b / 255.0), (CGFloat) (a / 255.0) ); + else +#endif + { + CGFloat components[4] = { (CGFloat)(r / 255.0), (CGFloat) (g / 255.0), (CGFloat) (b / 255.0), (CGFloat) (a / 255.0) } ; + col = CGColorCreate( wxMacGetGenericRGBColorSpace() , components ) ; + } + m_cgColour.reset( col ); +} + +#if wxMAC_USE_QUICKDRAW +void wxColour::InitRGBColor( const RGBColor& col ) +{ + m_red = col.red >> 8; + m_blue = col.blue >> 8; + m_green = col.green >> 8; + m_alpha = wxALPHA_OPAQUE; + CGColorRef cfcol; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( CGColorCreateGenericRGB ) + cfcol = CGColorCreateGenericRGB((CGFloat)(col.red / 65535.0), (CGFloat)(col.green / 65535.0), + (CGFloat)(col.blue / 65535.0), (CGFloat) 1.0 ); + else +#endif + { + CGFloat components[4] = { (CGFloat)(col.red / 65535.0), (CGFloat)(col.green / 65535.0), + (CGFloat)(col.blue / 65535.0), (CGFloat) 1.0 } ; + cfcol = CGColorCreate( wxMacGetGenericRGBColorSpace() , components ) ; + } + m_cgColour.reset( cfcol ); +} +#endif + +void wxColour::InitCGColorRef( CGColorRef col ) +{ + m_cgColour.reset( col ); + size_t noComp = CGColorGetNumberOfComponents( col ); + if ( noComp >= 1 && noComp <= 4 ) + { + // TODO verify whether we really are on a RGB color space + m_alpha = wxALPHA_OPAQUE; + const CGFloat *components = CGColorGetComponents( col ); + if ( noComp >= 3 ) + { + m_red = (int)(components[0]*255+0.5); + m_green = (int)(components[1]*255+0.5); + m_blue = (int)(components[2]*255+0.5); + if ( noComp == 4 ) + m_alpha = (int)(components[3]*255+0.5); + } + else + { + m_red = (int)(components[0]*255+0.5); + m_green = (int)(components[0]*255+0.5); + m_blue = (int)(components[0]*255+0.5); + } + } + else + { + m_alpha = wxALPHA_OPAQUE; + m_red = m_green = m_blue = 0; + } +} + +bool wxColour::operator == (const wxColour& colour) const +{ + return ( (IsOk() == colour.IsOk()) && (!IsOk() || + CGColorEqualToColor( m_cgColour, colour.m_cgColour ) ) ); +} + + diff --git a/src/osx/carbon/combobox.cpp b/src/osx/carbon/combobox.cpp new file mode 100644 index 0000000000..90ba52c9c8 --- /dev/null +++ b/src/osx/carbon/combobox.cpp @@ -0,0 +1,668 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/combobox.cpp +// Purpose: wxComboBox class +// Author: Stefan Csomor, Dan "Bud" Keith (composite combobox) +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_COMBOBOX + +#include "wx/combobox.h" + +#ifndef WX_PRECOMP + #include "wx/button.h" + #include "wx/menu.h" + #include "wx/containr.h" + #include "wx/toplevel.h" + #include "wx/textctrl.h" +#endif + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl) + +WX_DELEGATE_TO_CONTROL_CONTAINER(wxComboBox, wxControl) + +BEGIN_EVENT_TABLE(wxComboBox, wxControl) + WX_EVENT_TABLE_CONTROL_CONTAINER(wxComboBox) +END_EVENT_TABLE() + + +static int nextPopUpMenuId = 1000 ; + +MenuHandle NewUniqueMenu() +{ + MenuHandle handle = UMANewMenu(nextPopUpMenuId, wxString(wxT("Menu")), wxFont::GetDefaultEncoding() ); + nextPopUpMenuId++ ; + + return handle ; +} + + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// the margin between the text control and the choice +// margin should be bigger on OS X due to blue highlight +// around text control. +static const wxCoord MARGIN = 4; +// this is the border a focus rect on OSX is needing +static const int TEXTFOCUSBORDER = 3 ; + + +// ---------------------------------------------------------------------------- +// wxComboBoxText: text control forwards events to combobox +// ---------------------------------------------------------------------------- + +class wxComboBoxText : public wxTextCtrl +{ +public: + wxComboBoxText( wxComboBox * cb ) + : wxTextCtrl( cb , 1 ) + { + m_cb = cb; + SetTriggerOnSetValue( false ); + } + +protected: + void OnChar( wxKeyEvent& event ) + { + // Allows processing the tab key to go to the next control + if (event.GetKeyCode() == WXK_TAB) + { + wxNavigationKeyEvent NavEvent; + NavEvent.SetEventObject(this); + NavEvent.SetDirection(true); + NavEvent.SetWindowChange(false); + + // Get the parent of the combo and have it process the navigation? + if (m_cb->GetParent()->HandleWindowEvent(NavEvent)) + return; + } + + // send the event to the combobox class in case the user has bound EVT_CHAR + wxKeyEvent kevt(event); + kevt.SetEventObject(m_cb); + if (m_cb->HandleWindowEvent(kevt)) + // If the event was handled and not skipped then we're done + return; + + if ( event.GetKeyCode() == WXK_RETURN ) + { + wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_cb->GetId()); + event.SetString( GetValue() ); + event.SetInt( m_cb->GetSelection() ); + event.SetEventObject( m_cb ); + + // This will invoke the dialog default action, + // such as the clicking the default button. + if (!m_cb->HandleWindowEvent( event )) + { + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); + if ( tlw && tlw->GetDefaultItem() ) + { + wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton); + if ( def && def->IsEnabled() ) + { + wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() ); + event.SetEventObject(def); + def->Command(event); + } + } + + return; + } + } + + event.Skip(); + } + + void OnKeyUp( wxKeyEvent& event ) + { + event.SetEventObject(m_cb); + event.SetId(m_cb->GetId()); + if (! m_cb->HandleWindowEvent(event)) + event.Skip(); + } + + void OnKeyDown( wxKeyEvent& event ) + { + event.SetEventObject(m_cb); + event.SetId(m_cb->GetId()); + if (! m_cb->HandleWindowEvent(event)) + event.Skip(); + } + + void OnText( wxCommandEvent& event ) + { + event.SetEventObject(m_cb); + event.SetId(m_cb->GetId()); + if (! m_cb->HandleWindowEvent(event)) + event.Skip(); + } + +private: + wxComboBox *m_cb; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(wxComboBoxText, wxTextCtrl) + EVT_KEY_DOWN(wxComboBoxText::OnKeyDown) + EVT_CHAR(wxComboBoxText::OnChar) + EVT_KEY_UP(wxComboBoxText::OnKeyUp) + EVT_TEXT(wxID_ANY, wxComboBoxText::OnText) +END_EVENT_TABLE() + +class wxComboBoxChoice : public wxChoice +{ +public: + wxComboBoxChoice( wxComboBox *cb, int style ) + : wxChoice( cb , 1 , wxDefaultPosition , wxDefaultSize , 0 , NULL , style & (wxCB_SORT) ) + { + m_cb = cb; + } + + int GetPopupWidth() const + { + switch ( GetWindowVariant() ) + { + case wxWINDOW_VARIANT_NORMAL : + case wxWINDOW_VARIANT_LARGE : + return 24 ; + + default : + return 21 ; + } + } + +protected: + void OnChoice( wxCommandEvent& e ) + { + wxString s = e.GetString(); + + m_cb->DelegateChoice( s ); + wxCommandEvent event2(wxEVT_COMMAND_COMBOBOX_SELECTED, m_cb->GetId() ); + event2.SetInt(m_cb->GetSelection()); + event2.SetEventObject(m_cb); + event2.SetString(m_cb->GetStringSelection()); + m_cb->ProcessCommand(event2); + + // For consistency with MSW and GTK, also send a text updated event + // After all, the text is updated when a selection is made + wxCommandEvent TextEvent( wxEVT_COMMAND_TEXT_UPDATED, m_cb->GetId() ); + TextEvent.SetString( m_cb->GetStringSelection() ); + TextEvent.SetEventObject( m_cb ); + m_cb->ProcessCommand( TextEvent ); + } + + virtual wxSize DoGetBestSize() const + { + wxSize sz = wxChoice::DoGetBestSize() ; + if (! m_cb->HasFlag(wxCB_READONLY) ) + sz.x = GetPopupWidth() ; + + return sz ; + } + +private: + wxComboBox *m_cb; + + friend class wxComboBox; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice) + EVT_CHOICE(wxID_ANY, wxComboBoxChoice::OnChoice) +END_EVENT_TABLE() + +wxComboBox::~wxComboBox() +{ + // delete the controls now, don't leave them alive even though they would + // still be eventually deleted by our parent - but it will be too late, the + // user code expects them to be gone now + if (m_text != NULL) + { + delete m_text; + m_text = NULL; + } + + if (m_choice != NULL) + { + delete m_choice; + m_choice = NULL; + } +} + +// ---------------------------------------------------------------------------- +// geometry +// ---------------------------------------------------------------------------- + +wxSize wxComboBox::DoGetBestSize() const +{ + if (!m_choice && !m_text) + return GetSize(); + + wxSize size = m_choice->GetBestSize(); + + if ( m_text != NULL ) + { + wxSize sizeText = m_text->GetBestSize(); + if (sizeText.y > size.y) + size.y = sizeText.y; + + size.x = m_choice->GetPopupWidth() + sizeText.x + MARGIN; + size.x += TEXTFOCUSBORDER ; + size.y += 2 * TEXTFOCUSBORDER ; + } + else + { + // clipping is too tight + size.y += 1 ; + } + + return size; +} + +void wxComboBox::DoMoveWindow(int x, int y, int width, int height) +{ + wxControl::DoMoveWindow( x, y, width , height ); + + if ( m_text == NULL ) + { + // we might not be fully constructed yet, therefore watch out... + if ( m_choice ) + m_choice->SetSize(0, 0 , width, -1); + } + else + { + wxCoord wText = width - m_choice->GetPopupWidth() - MARGIN; + m_text->SetSize(TEXTFOCUSBORDER, TEXTFOCUSBORDER, wText, -1); + + // put it at an inset of 1 to have outer area shadows drawn as well + m_choice->SetSize(TEXTFOCUSBORDER + wText + MARGIN - 1 , TEXTFOCUSBORDER, m_choice->GetPopupWidth() , -1); + } +} + +// ---------------------------------------------------------------------------- +// operations forwarded to the subcontrols +// ---------------------------------------------------------------------------- + +bool wxComboBox::Enable(bool enable) +{ + if ( !wxControl::Enable(enable) ) + return false; + + if (m_text) + m_text->Enable(enable); + + return true; +} + +bool wxComboBox::Show(bool show) +{ + if ( !wxControl::Show(show) ) + return false; + + return true; +} + +void wxComboBox::DelegateTextChanged( const wxString& value ) +{ + SetStringSelection( value ); +} + +void wxComboBox::DelegateChoice( const wxString& value ) +{ + SetStringSelection( value ); +} + +void wxComboBox::Init() +{ + WX_INIT_CONTROL_CONTAINER(); +} + +bool wxComboBox::Create(wxWindow *parent, + wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + const wxArrayString& choices, + long style, + const wxValidator& validator, + const wxString& name) +{ + if ( !Create( parent, id, value, pos, size, 0, NULL, + style, validator, name ) ) + return false; + + Append(choices); + + return true; +} + +bool wxComboBox::Create(wxWindow *parent, + wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + int n, + const wxString choices[], + long style, + const wxValidator& validator, + const wxString& name) +{ + if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style , + validator, name) ) + { + return false; + } + + wxSize csize = size; + if ( style & wxCB_READONLY ) + { + m_text = NULL; + } + else + { + m_text = new wxComboBoxText(this); + if ( size.y == -1 ) + { + csize.y = m_text->GetSize().y ; + csize.y += 2 * TEXTFOCUSBORDER ; + } + } + m_choice = new wxComboBoxChoice(this, style ); + + DoSetSize(pos.x, pos.y, csize.x, csize.y); + + Append( n, choices ); + + // Needed because it is a wxControlWithItems + SetInitialSize(size); + SetStringSelection(value); + + return true; +} + +wxString wxComboBox::GetValue() const +{ + wxString result; + + if ( m_text == NULL ) + result = m_choice->GetString( m_choice->GetSelection() ); + else + result = m_text->GetValue(); + + return result; +} + +unsigned int wxComboBox::GetCount() const +{ + return m_choice->GetCount() ; +} + +void wxComboBox::SetValue(const wxString& value) +{ + if ( HasFlag(wxCB_READONLY) ) + SetStringSelection( value ) ; + else + m_text->SetValue( value ); +} + +void wxComboBox::WriteText(const wxString& text) +{ + m_text->WriteText(text); +} + +void wxComboBox::GetSelection(long *from, long *to) const +{ + m_text->GetSelection(from, to); +} + +// Clipboard operations + +void wxComboBox::Copy() +{ + if ( m_text != NULL ) + m_text->Copy(); +} + +void wxComboBox::Cut() +{ + if ( m_text != NULL ) + m_text->Cut(); +} + +void wxComboBox::Paste() +{ + if ( m_text != NULL ) + m_text->Paste(); +} + +void wxComboBox::SetEditable(bool editable) +{ + if ( ( m_text == NULL ) && editable ) + { + m_text = new wxComboBoxText( this ); + } + else if ( ( m_text != NULL ) && !editable ) + { + delete m_text; + m_text = NULL; + } + + int currentX, currentY; + GetPosition( ¤tX, ¤tY ); + + int currentW, currentH; + GetSize( ¤tW, ¤tH ); + + DoMoveWindow( currentX, currentY, currentW, currentH ); +} + +void wxComboBox::SetInsertionPoint(long pos) +{ + if ( m_text ) + m_text->SetInsertionPoint(pos); +} + +void wxComboBox::SetInsertionPointEnd() +{ + if ( m_text ) + m_text->SetInsertionPointEnd(); +} + +long wxComboBox::GetInsertionPoint() const +{ + if ( m_text ) + return m_text->GetInsertionPoint(); + return 0; +} + +wxTextPos wxComboBox::GetLastPosition() const +{ + if ( m_text ) + return m_text->GetLastPosition(); + return 0; +} + +void wxComboBox::Replace(long from, long to, const wxString& value) +{ + if ( m_text ) + m_text->Replace(from,to,value); +} + +void wxComboBox::Remove(long from, long to) +{ + if ( m_text ) + m_text->Remove(from,to); +} + +void wxComboBox::SetSelection(long from, long to) +{ + if ( m_text ) + m_text->SetSelection(from,to); +} + +int wxComboBox::DoInsertItems(const wxArrayStringsAdapter& items, + unsigned int pos, + void **clientData, + wxClientDataType type) +{ + return m_choice->DoInsertItems(items, pos, clientData, type); +} + +void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData) +{ + return m_choice->DoSetItemClientData( n , clientData ) ; +} + +void* wxComboBox::DoGetItemClientData(unsigned int n) const +{ + return m_choice->DoGetItemClientData( n ) ; +} + +wxClientDataType wxComboBox::GetClientDataType() const +{ + return m_choice->GetClientDataType(); +} + +void wxComboBox::SetClientDataType(wxClientDataType clientDataItemsType) +{ + m_choice->SetClientDataType(clientDataItemsType); +} + +void wxComboBox::DoDeleteOneItem(unsigned int n) +{ + m_choice->DoDeleteOneItem( n ); +} + +void wxComboBox::DoClear() +{ + m_choice->DoClear(); +} + +int wxComboBox::GetSelection() const +{ + return m_choice->GetSelection(); +} + +void wxComboBox::SetSelection(int n) +{ + m_choice->SetSelection( n ); + + if ( m_text != NULL ) + m_text->SetValue(n != wxNOT_FOUND ? GetString(n) : wxString(wxEmptyString)); +} + +int wxComboBox::FindString(const wxString& s, bool bCase) const +{ + return m_choice->FindString( s, bCase ); +} + +wxString wxComboBox::GetString(unsigned int n) const +{ + return m_choice->GetString( n ); +} + +wxString wxComboBox::GetStringSelection() const +{ + int sel = GetSelection(); + if (sel != wxNOT_FOUND) + return wxString(this->GetString((unsigned int)sel)); + else + return wxEmptyString; +} + +void wxComboBox::SetString(unsigned int n, const wxString& s) +{ + m_choice->SetString( n , s ); +} + +bool wxComboBox::IsEditable() const +{ + return m_text != NULL && !HasFlag(wxCB_READONLY); +} + +void wxComboBox::Undo() +{ + if (m_text != NULL) + m_text->Undo(); +} + +void wxComboBox::Redo() +{ + if (m_text != NULL) + m_text->Redo(); +} + +void wxComboBox::SelectAll() +{ + if (m_text != NULL) + m_text->SelectAll(); +} + +bool wxComboBox::CanCopy() const +{ + if (m_text != NULL) + return m_text->CanCopy(); + else + return false; +} + +bool wxComboBox::CanCut() const +{ + if (m_text != NULL) + return m_text->CanCut(); + else + return false; +} + +bool wxComboBox::CanPaste() const +{ + if (m_text != NULL) + return m_text->CanPaste(); + else + return false; +} + +bool wxComboBox::CanUndo() const +{ + if (m_text != NULL) + return m_text->CanUndo(); + else + return false; +} + +bool wxComboBox::CanRedo() const +{ + if (m_text != NULL) + return m_text->CanRedo(); + else + return false; +} + +wxInt32 wxComboBox::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ +/* + For consistency with other platforms, clicking in the text area does not constitute a selection + wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId ); + event.SetInt(GetSelection()); + event.SetEventObject(this); + event.SetString(GetStringSelection()); + ProcessCommand(event); +*/ + + return noErr ; +} + +#endif // wxUSE_COMBOBOX diff --git a/src/osx/carbon/combobxc.cpp b/src/osx/carbon/combobxc.cpp new file mode 100644 index 0000000000..c494f9fada --- /dev/null +++ b/src/osx/carbon/combobxc.cpp @@ -0,0 +1,793 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/combobxc.cpp +// Purpose: wxComboBox class using HIView ComboBox +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/combobox.h" + +#ifndef WX_PRECOMP + #include "wx/button.h" + #include "wx/menu.h" +#endif + +#include "wx/mac/uma.h" +#if TARGET_API_MAC_OSX +#ifndef __HIVIEW__ + #include +#endif +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl) + +#if TARGET_API_MAC_OSX +#define USE_HICOMBOBOX 1 //use hi combobox define +#else +#define USE_HICOMBOBOX 0 +#endif + +static int nextPopUpMenuId = 1000; +MenuHandle NewUniqueMenu() +{ + MenuHandle handle = NewMenu( nextPopUpMenuId , "\pMenu" ); + nextPopUpMenuId++; + return handle; +} + +#if USE_HICOMBOBOX +static const EventTypeSpec eventList[] = +{ + { kEventClassTextField , kEventTextAccepted } , +}; + +static pascal OSStatus wxMacComboBoxEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr; + wxComboBox* cb = (wxComboBox*) data; + + wxMacCarbonEvent cEvent( event ); + + switch( cEvent.GetClass() ) + { + case kEventClassTextField : + switch( cEvent.GetKind() ) + { + case kEventTextAccepted : + { + wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, cb->GetId() ); + event.SetInt( cb->GetSelection() ); + event.SetString( cb->GetStringSelection() ); + event.SetEventObject( cb ); + cb->HandleWindowEvent( event ); + } + break; + default : + break; + } + break; + default : + break; + } + + + return result; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacComboBoxEventHandler ) + +#endif + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// the margin between the text control and the choice +static const wxCoord MARGIN = 2; +#if TARGET_API_MAC_OSX +static const int POPUPWIDTH = 24; +#else +static const int POPUPWIDTH = 18; +#endif +static const int POPUPHEIGHT = 23; + +// ---------------------------------------------------------------------------- +// wxComboBoxText: text control forwards events to combobox +// ---------------------------------------------------------------------------- + +class wxComboBoxText : public wxTextCtrl +{ +public: + wxComboBoxText( wxComboBox * cb ) + : wxTextCtrl( cb , 1 ) + { + m_cb = cb; + } + +protected: + void OnChar( wxKeyEvent& event ) + { + if ( event.GetKeyCode() == WXK_RETURN ) + { + wxString value = GetValue(); + + if ( m_cb->GetCount() == 0 ) + { + // make Enter generate "selected" event if there is only one item + // in the combobox - without it, it's impossible to select it at + // all! + wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, m_cb->GetId() ); + event.SetInt( 0 ); + event.SetString( value ); + event.SetEventObject( m_cb ); + m_cb->HandleWindowEvent( event ); + } + else + { + // add the item to the list if it's not there yet + if ( m_cb->FindString(value) == wxNOT_FOUND ) + { + m_cb->Append(value); + m_cb->SetStringSelection(value); + + // and generate the selected event for it + wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, m_cb->GetId() ); + event.SetInt( m_cb->GetCount() - 1 ); + event.SetString( value ); + event.SetEventObject( m_cb ); + m_cb->HandleWindowEvent( event ); + } + + // This will invoke the dialog default action, such + // as the clicking the default button. + + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); + if ( tlw && tlw->GetDefaultItem() ) + { + wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton); + if ( def && def->IsEnabled() ) + { + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() ); + event.SetEventObject(def); + def->Command(event); + return; + } + } + + return; + } + } + + event.Skip(); + } +private: + wxComboBox *m_cb; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(wxComboBoxText, wxTextCtrl) + EVT_CHAR( wxComboBoxText::OnChar) +END_EVENT_TABLE() + +class wxComboBoxChoice : public wxChoice +{ +public: + wxComboBoxChoice(wxComboBox *cb, int style) + : wxChoice( cb , 1 ) + { + m_cb = cb; + } + +protected: + void OnChoice( wxCommandEvent& e ) + { + wxString s = e.GetString(); + + m_cb->DelegateChoice( s ); + wxCommandEvent event2(wxEVT_COMMAND_COMBOBOX_SELECTED, m_cb->GetId() ); + event2.SetInt(m_cb->GetSelection()); + event2.SetEventObject(m_cb); + event2.SetString(m_cb->GetStringSelection()); + m_cb->ProcessCommand(event2); + } + virtual wxSize DoGetBestSize() const + { + wxSize sz = wxChoice::DoGetBestSize(); + sz.x = POPUPWIDTH; + return sz; + } + +private: + wxComboBox *m_cb; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(wxComboBoxChoice, wxChoice) + EVT_CHOICE(wxID_ANY, wxComboBoxChoice::OnChoice) +END_EVENT_TABLE() + +wxComboBox::~wxComboBox() +{ + // delete the controls now, don't leave them alive even though they would + // still be eventually deleted by our parent - but it will be too late, the + // user code expects them to be gone now + if (m_text != NULL) { + delete m_text; + m_text = NULL; + } + if (m_choice != NULL) { + delete m_choice; + m_choice = NULL; + } +} + + +// ---------------------------------------------------------------------------- +// geometry +// ---------------------------------------------------------------------------- + +wxSize wxComboBox::DoGetBestSize() const +{ +#if USE_HICOMBOBOX + return wxControl::DoGetBestSize(); +#else + wxSize size = m_choice->GetBestSize(); + + if ( m_text != NULL ) + { + wxSize sizeText = m_text->GetBestSize(); + + size.x = POPUPWIDTH + sizeText.x + MARGIN; + } + + return size; +#endif +} + +void wxComboBox::DoMoveWindow(int x, int y, int width, int height) { +#if USE_HICOMBOBOX + wxControl::DoMoveWindow(x, y, width, height); +#else + height = POPUPHEIGHT; + + wxControl::DoMoveWindow(x, y, width, height); + + if ( m_text == NULL ) + { + // we might not be fully constructed yet, therefore watch out... + if ( m_choice ) + m_choice->SetSize(0, 0 , width, wxDefaultCoord); + } + else + { + wxCoord wText = width - POPUPWIDTH - MARGIN; + m_text->SetSize(0, 0, wText, height); + m_choice->SetSize(0 + wText + MARGIN, 0, POPUPWIDTH, wxDefaultCoord); + } +#endif +} + + + +// ---------------------------------------------------------------------------- +// operations forwarded to the subcontrols +// ---------------------------------------------------------------------------- + +bool wxComboBox::Enable(bool enable) +{ + if ( !wxControl::Enable(enable) ) + return false; + + return true; +} + +bool wxComboBox::Show(bool show) +{ + if ( !wxControl::Show(show) ) + return false; + + return true; +} + +void wxComboBox::SetFocus() +{ +#if USE_HICOMBOBOX + wxControl::SetFocus(); +#else + if ( m_text != NULL) { + m_text->SetFocus(); + } +#endif +} + + +void wxComboBox::DelegateTextChanged( const wxString& value ) +{ + SetStringSelection( value ); +} + + +void wxComboBox::DelegateChoice( const wxString& value ) +{ + SetStringSelection( value ); +} + + +bool wxComboBox::Create(wxWindow *parent, wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + const wxArrayString& choices, + long style, + const wxValidator& validator, + const wxString& name) +{ + wxCArrayString chs( choices ); + + return Create( parent, id, value, pos, size, chs.GetCount(), + chs.GetStrings(), style, validator, name ); +} + + +bool wxComboBox::Create(wxWindow *parent, wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + int n, const wxString choices[], + long style, + const wxValidator& validator, + const wxString& name) +{ + m_text = NULL; + m_choice = NULL; +#if USE_HICOMBOBOX + m_macIsUserPane = false; +#endif + if ( !wxControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, style , + wxDefaultValidator, name) ) + { + return false; + } +#if USE_HICOMBOBOX + Rect bounds = wxMacGetBoundsForControl( this , pos , size ); + HIRect hiRect; + + hiRect.origin.x = 20; //bounds.left; + hiRect.origin.y = 25; //bounds.top; + hiRect.size.width = 120;// bounds.right - bounds.left; + hiRect.size.height = 24; + + //For some reason, this code causes the combo box not to be displayed at all. + //hiRect.origin.x = bounds.left; + //hiRect.origin.y = bounds.top; + //hiRect.size.width = bounds.right - bounds.left; + //hiRect.size.height = bounds.bottom - bounds.top; + //printf("left = %d, right = %d, top = %d, bottom = %d\n", bounds.left, bounds.right, bounds.top, bounds.bottom); + //printf("x = %d, y = %d, width = %d, height = %d\n", hibounds.origin.x, hibounds.origin.y, hibounds.size.width, hibounds.size.height); + m_peer = new wxMacControl(this); + verify_noerr( HIComboBoxCreate( &hiRect, CFSTR(""), NULL, NULL, kHIComboBoxStandardAttributes, m_peer->GetControlRefAddr() ) ); + + + m_peer->SetMinimum( 0 ); + m_peer->SetMaximum( 100); + if ( n > 0 ) + m_peer->SetValue( 1 ); + + MacPostControlCreate(pos,size); + + Append( choices[ i ] ); + + HIViewSetVisible( m_peer->GetControlRef(), true ); + SetSelection(0); + EventHandlerRef comboEventHandler; + InstallControlEventHandler( m_peer->GetControlRef(), GetwxMacComboBoxEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, this, + (EventHandlerRef *)&comboEventHandler); +#else + m_choice = new wxComboBoxChoice(this, style ); + m_choice->SetMinSize( wxSize( POPUPWIDTH , POPUPHEIGHT ) ); + + wxSize csize = size; + if ( style & wxCB_READONLY ) + { + m_text = NULL; + } + else + { + m_text = new wxComboBoxText(this); + if ( size.y == wxDefaultCoord ) { + csize.y = m_text->GetSize().y; + } + } + + DoSetSize(pos.x, pos.y, csize.x, csize.y); + + m_choice->Append( n, choices ); + SetInitialSize(csize); // Needed because it is a wxControlWithItems +#endif + + return true; +} + +wxString wxComboBox::GetValue() const +{ +#if USE_HICOMBOBOX + CFStringRef myString; + HIComboBoxCopyTextItemAtIndex( m_peer->GetControlRef(), (CFIndex)GetSelection(), &myString ); + return wxMacCFStringHolder( myString, GetFont().GetEncoding() ).AsString(); +#else + wxString result; + + if ( m_text == NULL ) + { + result = m_choice->GetString( m_choice->GetSelection() ); + } + else + { + result = m_text->GetValue(); + } + + return result; +#endif +} + +void wxComboBox::SetValue(const wxString& value) +{ +#if USE_HICOMBOBOX + +#else + int s = FindString (value); + if (s == wxNOT_FOUND && !HasFlag(wxCB_READONLY) ) + { + m_choice->Append(value); + } + SetStringSelection( value ); +#endif +} + +// Clipboard operations +void wxComboBox::Copy() +{ + if ( m_text != NULL ) + { + m_text->Copy(); + } +} + +void wxComboBox::Cut() +{ + if ( m_text != NULL ) + { + m_text->Cut(); + } +} + +void wxComboBox::Paste() +{ + if ( m_text != NULL ) + { + m_text->Paste(); + } +} + +void wxComboBox::SetEditable(bool editable) +{ + if ( ( m_text == NULL ) && editable ) + { + m_text = new wxComboBoxText( this ); + } + else if ( ( m_text != NULL ) && !editable ) + { + delete m_text; + m_text = NULL; + } + + int currentX, currentY; + GetPosition( ¤tX, ¤tY ); + + int currentW, currentH; + GetSize( ¤tW, ¤tH ); + + DoMoveWindow( currentX, currentY, currentW, currentH ); +} + +void wxComboBox::SetInsertionPoint(long pos) +{ + // TODO +} + +void wxComboBox::SetInsertionPointEnd() +{ + // TODO +} + +long wxComboBox::GetInsertionPoint() const +{ + // TODO + return 0; +} + +wxTextPos wxComboBox::GetLastPosition() const +{ + // TODO + return 0; +} + +void wxComboBox::Replace(long from, long to, const wxString& value) +{ + // TODO +} + +void wxComboBox::Remove(long from, long to) +{ + // TODO +} + +void wxComboBox::SetSelection(long from, long to) +{ + // TODO +} + +int wxComboBox::DoInsertItems(const wxArrayStringsAdapter& items, + unsigned int pos, + void **clientData, wxClientDataType type) +{ +#if USE_HICOMBOBOX + const unsigned int count = items.GetCount(); + for ( unsigned int i = 0; i < count; ++i, ++pos ) + { + HIComboBoxInsertTextItemAtIndex(m_peer->GetControlRef(), + (CFIndex)pos, + wxMacCFStringHolder(items[i], + GetFont().GetEncoding())); + AssignNewItemClientData(pos, clientData, i, type); + } + + //SetControl32BitMaximum( m_peer->GetControlRef(), GetCount() ); + + return pos - 1; +#else + return m_choice->DoInsertItems( items, pos, clientData, type ); +#endif +} + +void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData) +{ +#if USE_HICOMBOBOX + return; //TODO +#else + return m_choice->DoSetItemClientData( n , clientData ); +#endif +} + +void* wxComboBox::DoGetItemClientData(unsigned int n) const +{ +#if USE_HICOMBOBOX + return NULL; //TODO +#else + return m_choice->DoGetItemClientData( n ); +#endif +} + +unsigned int wxComboBox::GetCount() const { +#if USE_HICOMBOBOX + return (unsigned int) HIComboBoxGetItemCount( m_peer->GetControlRef() ); +#else + return m_choice->GetCount(); +#endif +} + +void wxComboBox::DoDeleteOneItem(unsigned int n) +{ +#if USE_HICOMBOBOX + HIComboBoxRemoveItemAtIndex( m_peer->GetControlRef(), (CFIndex)n ); +#else + m_choice->Delete( n ); +#endif +} + +void wxComboBox::DoClear() +{ +#if USE_HICOMBOBOX + for ( CFIndex i = GetCount() - 1; i >= 0; ++ i ) + verify_noerr( HIComboBoxRemoveItemAtIndex( m_peer->GetControlRef(), i ) ); + m_peer->SetData(kHIComboBoxEditTextPart,kControlEditTextCFStringTag,CFSTR("")); +#else + m_choice->Clear(); +#endif +} + +int wxComboBox::GetSelection() const +{ +#if USE_HICOMBOBOX + return FindString( GetStringSelection() ); +#else + return m_choice->GetSelection(); +#endif +} + +void wxComboBox::SetSelection(int n) +{ +#if USE_HICOMBOBOX + SetControl32BitValue( m_peer->GetControlRef() , n + 1 ); +#else + m_choice->SetSelection( n ); + + if ( m_text != NULL ) + { + m_text->SetValue(GetString(n)); + } +#endif +} + +int wxComboBox::FindString(const wxString& s, bool bCase) const +{ +#if USE_HICOMBOBOX + for( unsigned int i = 0 ; i < GetCount() ; i++ ) + { + if (GetString(i).IsSameAs(s, bCase) ) + return i ; + } + return wxNOT_FOUND; +#else + return m_choice->FindString( s, bCase ); +#endif +} + +wxString wxComboBox::GetString(unsigned int n) const +{ +#if USE_HICOMBOBOX + CFStringRef itemText; + HIComboBoxCopyTextItemAtIndex( m_peer->GetControlRef(), (CFIndex)n, &itemText ); + return wxMacCFStringHolder(itemText).AsString(); +#else + return m_choice->GetString( n ); +#endif +} + +wxString wxComboBox::GetStringSelection() const +{ +#if USE_HICOMBOBOX + return wxMacCFStringHolder(m_peer->GetData(kHIComboBoxEditTextPart,kControlEditTextCFStringTag)).AsString(); +#else + int sel = GetSelection (); + if (sel != wxNOT_FOUND) + return wxString(this->GetString((unsigned int)sel)); + else + return wxEmptyString; +#endif +} + +void wxComboBox::SetString(unsigned int n, const wxString& s) +{ +#if USE_HICOMBOBOX + verify_noerr ( HIComboBoxInsertTextItemAtIndex( m_peer->GetControlRef(), (CFIndex) n, + wxMacCFStringHolder(s, GetFont().GetEncoding()) ) ); + verify_noerr ( HIComboBoxRemoveItemAtIndex( m_peer->GetControlRef(), (CFIndex) n + 1 ) ); +#else + m_choice->SetString( n , s ); +#endif +} + +bool wxComboBox::IsEditable() const +{ +#if USE_HICOMBOBOX + // TODO + return !HasFlag(wxCB_READONLY); +#else + return m_text != NULL && !HasFlag(wxCB_READONLY); +#endif +} + +void wxComboBox::Undo() +{ +#if USE_HICOMBOBOX + // TODO +#else + if (m_text != NULL) + m_text->Undo(); +#endif +} + +void wxComboBox::Redo() +{ +#if USE_HICOMBOBOX + // TODO +#else + if (m_text != NULL) + m_text->Redo(); +#endif +} + +void wxComboBox::SelectAll() +{ +#if USE_HICOMBOBOX + // TODO +#else + if (m_text != NULL) + m_text->SelectAll(); +#endif +} + +bool wxComboBox::CanCopy() const +{ +#if USE_HICOMBOBOX + // TODO + return false; +#else + if (m_text != NULL) + return m_text->CanCopy(); + else + return false; +#endif +} + +bool wxComboBox::CanCut() const +{ +#if USE_HICOMBOBOX + // TODO + return false; +#else + if (m_text != NULL) + return m_text->CanCut(); + else + return false; +#endif +} + +bool wxComboBox::CanPaste() const +{ +#if USE_HICOMBOBOX + // TODO + return false; +#else + if (m_text != NULL) + return m_text->CanPaste(); + else + return false; +#endif +} + +bool wxComboBox::CanUndo() const +{ +#if USE_HICOMBOBOX + // TODO + return false; +#else + if (m_text != NULL) + return m_text->CanUndo(); + else + return false; +#endif +} + +bool wxComboBox::CanRedo() const +{ +#if USE_HICOMBOBOX + // TODO + return false; +#else + if (m_text != NULL) + return m_text->CanRedo(); + else + return false; +#endif +} + +wxInt32 wxComboBox::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCommandEvent event(wxEVT_COMMAND_COMBOBOX_SELECTED, m_windowId ); + event.SetInt(GetSelection()); + event.SetEventObject(this); + event.SetString(GetStringSelection()); + ProcessCommand(event); + return noErr; +} diff --git a/src/osx/carbon/control.cpp b/src/osx/carbon/control.cpp new file mode 100644 index 0000000000..f787ea9812 --- /dev/null +++ b/src/osx/carbon/control.cpp @@ -0,0 +1,97 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/control.cpp +// Purpose: wxControl class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/control.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/panel.h" + #include "wx/dc.h" + #include "wx/dcclient.h" + #include "wx/button.h" + #include "wx/dialog.h" + #include "wx/scrolbar.h" + #include "wx/stattext.h" + #include "wx/statbox.h" + #include "wx/radiobox.h" + #include "wx/sizer.h" +#endif // WX_PRECOMP + +#include "wx/notebook.h" +#include "wx/tabctrl.h" +#include "wx/spinbutt.h" + +#include "wx/mac/uma.h" +#include "wx/mac/private.h" + +IMPLEMENT_ABSTRACT_CLASS(wxControl, wxWindow) + + +wxControl::wxControl() +{ +} + +bool wxControl::Create( wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name ) +{ + bool rval = wxWindow::Create( parent, id, pos, size, style, name ); + +#if 0 + // no automatic inheritance as we most often need transparent backgrounds + if ( parent ) + { + m_backgroundColour = parent->GetBackgroundColour(); + m_foregroundColour = parent->GetForegroundColour(); + } +#endif + +#if wxUSE_VALIDATORS + if (rval) + SetValidator( validator ); +#endif + + return rval; +} + +wxControl::~wxControl() +{ + m_isBeingDeleted = true; +} + +bool wxControl::ProcessCommand( wxCommandEvent &event ) +{ + // Tries: + // 1) OnCommand, starting at this window and working up parent hierarchy + // 2) OnCommand then calls ProcessEvent to search the event tables. + return HandleWindowEvent( event ); +} + +void wxControl::OnKeyDown( wxKeyEvent &WXUNUSED(event) ) +{ + if ( m_peer == NULL || !m_peer->Ok() ) + return; + + UInt32 keyCode, modifiers; + char charCode; + + GetEventParameter( (EventRef)wxTheApp->MacGetCurrentEvent(), kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode ); + GetEventParameter( (EventRef)wxTheApp->MacGetCurrentEvent(), kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &charCode ); + GetEventParameter( (EventRef)wxTheApp->MacGetCurrentEvent(), kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers ); + + m_peer->HandleKey( keyCode, charCode, modifiers ); +} diff --git a/src/osx/carbon/corersrc.r b/src/osx/carbon/corersrc.r new file mode 100644 index 0000000000..676a4d3d2a --- /dev/null +++ b/src/osx/carbon/corersrc.r @@ -0,0 +1,159 @@ +#ifdef __UNIX__ + #include +#else + #include + #if UNIVERSAL_INTERFACES_VERSION > 0x320 + #include + #endif +#endif + +resource 'ldes' ( 128 ) +{ + versionZero + { + 0 , + 0 , + 0 , + 0 , + hasVertScroll , + noHorizScroll , + 0 , + noGrowSpace , + } +} ; + +resource 'ldes' ( 129 ) +{ + versionZero + { + 0 , + 0 , + 0 , + 0 , + hasVertScroll , + hasHorizScroll , + 0 , + noGrowSpace , + } +} ; + +data 'CURS' (10) { + $"0000 03E0 0630 0808 1004 31C6 2362 2222" + $"2362 31C6 1004 0808 0630 03E0 0000 0000" + $"0000 03E0 07F0 0FF8 1FFC 3FFE 3FFE 3FFE" + $"3FFE 3FFE 1FFC 0FF8 07F0 03E0 0000 0000" + $"0007 0008" +}; + +data 'CURS' (11) { + $"0000 0000 0000 0000 0000 0000 0000 0000" + $"0000 0000 0000 0000 0000 0000 0000 0000" + $"0000 0000 0000 0000 0000 0000 0000 0000" + $"0000 0000 0000 0000 0000 0000 0000 0000" + $"0000 0000" +}; + +data 'CURS' (12) { + $"00F0 0088 0108 0190 0270 0220 0440 0440" + $"0880 0880 1100 1E00 1C00 1800 1000 0000" + $"00F0 00F8 01F8 01F0 03F0 03E0 07C0 07C0" + $"0F80 0F80 1F00 1E00 1C00 1800 1000 0000" + $"000E 0003" +}; + +data 'CURS' (13) { + $"0000 1E00 2100 4080 4080 4080 4080 2180" + $"1FC0 00E0 0070 0038 001C 000E 0006 0000" + $"3F00 7F80 FFC0 FFC0 FFC0 FFC0 FFC0 7FC0" + $"3FE0 1FF0 00F8 007C 003E 001F 000F 0007" + $"0004 0004" +}; + +data 'CURS' (14) { + $"0000 07E0 1FF0 3838 3C0C 6E0E 6706 6386" + $"61C6 60E6 7076 303C 1C1C 0FF8 07E0 0000" + $"0540 0FF0 3FF8 3C3C 7E0E FF0F 6F86 E7C7" + $"63E6 E1F7 70FE 707E 3C3C 1FFC 0FF0 0540" + $"0007 0007" +}; + +data 'CURS' (15) { + $"0000 0380 0380 0380 0380 0380 0380 0FE0" + $"1FF0 1FF0 0000 1FF0 1FF0 1550 1550 1550" + $"07C0 07C0 07C0 07C0 07C0 07C0 0FE0 1FF0" + $"3FF8 3FF8 3FF8 3FF8 3FF8 3FF8 3FF8 3FF8" + $"000B 0007" +}; + +data 'CURS' (16) { + $"00C0 0140 0640 08C0 3180 47FE 8001 8001" + $"81FE 8040 01C0 0040 03C0 C080 3F80 0000" + $"00C0 01C0 07C0 0FC0 3F80 7FFE FFFF FFFF" + $"FFFE FFC0 FFC0 FFC0 FFC0 FF80 3F80 0000" + $"0006 000F" +}; + +data 'CURS' (17) { + $"0100 0280 0260 0310 018C 7FE3 8000 8000" + $"7F80 0200 0380 0200 03C0 0107 01F8 0000" + $"0100 0380 03E0 03F0 01FC 7FFF FFFF FFFF" + $"FFFF 03FF 03FF 03FF 03FF 01FF 01F8 0000" + $"0006 0000" +}; + +data 'CURS' (18) { + $"0000 4078 60FC 71CE 7986 7C06 7E0E 7F1C" + $"7FB8 7C30 6C30 4600 0630 0330 0300 0000" + $"C078 E0FC F1FE FBFF FFCF FF8F FF1F FFBE" + $"FFFC FE78 FF78 EFF8 CFF8 87F8 07F8 0300" + $"0001 0001" +}; + +data 'CURS' (19) { + $"0000 0002 0006 000E 001E 003E 007E 00FE" + $"01FE 003E 0036 0062 0060 00C0 00C0 0000" + $"0003 0007 000F 001F 003F 007F 00FF 01FF" + $"03FF 07FF 007F 00F7 00F3 01E1 01E0 01C0" + $"0001 000E" +}; + +data 'CURS' (20) { + $"0000 0080 01C0 03E0 0080 0080 0080 1FFC" + $"1FFC 0080 0080 0080 03E0 01C0 0080 0000" + $"0080 01C0 03E0 07F0 0FF8 01C0 3FFE 3FFE" + $"3FFE 3FFE 01C0 0FF8 07F0 03E0 01C0 0080" + $"0007 0008" +}; + +data 'CURS' (21) { + $"0000 0080 01C0 03E0 0080 0888 188C 3FFE" + $"188C 0888 0080 03E0 01C0 0080 0000 0000" + $"0080 01C0 03E0 07F0 0BE8 1DDC 3FFE 7FFF" + $"3FFE 1DDC 0BE8 07F0 03E0 01C0 0080 0000" + $"0007 0008" +}; + +data 'CURS' (22) { + $"0000 001E 000E 060E 0712 03A0 01C0 00E0" + $"0170 1238 1C18 1C00 1E00 0000 0000 0000" + $"007F 003F 0E1F 0F0F 0F97 07E3 03E1 21F0" + $"31F8 3A7C 3C3C 3E1C 3F00 3F80 0000 0000" + $"0006 0009" +}; + +data 'CURS' (23) { + $"0000 7800 7000 7060 48E0 05C0 0380 0700" + $"0E80 1C48 1838 0038 0078 0000 0000 0000" + $"FE00 FC00 F870 F0F0 E9F0 C7E0 87C0 0F84" + $"1F8C 3E5C 3C3C 387C 00FC 01FC 0000 0000" + $"0006 0006" +}; + +data 'CURS' (24) { + $"0006 000E 001C 0018 0020 0040 00F8 0004" + $"1FF4 200C 2AA8 1FF0 1F80 3800 6000 8000" + $"000F 001F 003E 007C 0070 00E0 01FC 3FF6" + $"7FF6 7FFE 7FFC 7FF8 3FF0 7FC0 F800 E000" + $"000A 0006" +}; + diff --git a/src/osx/carbon/cursor.cpp b/src/osx/carbon/cursor.cpp new file mode 100644 index 0000000000..36e40a7de9 --- /dev/null +++ b/src/osx/carbon/cursor.cpp @@ -0,0 +1,697 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/cursor.cpp +// Purpose: wxCursor class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/cursor.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/icon.h" + #include "wx/image.h" +#endif // WX_PRECOMP + +#include "wx/xpmdecod.h" + +#include "wx/mac/private.h" + + +IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxGDIObject) + + +class WXDLLEXPORT wxCursorRefData: public wxGDIRefData +{ +public: + wxCursorRefData(); + wxCursorRefData(const wxCursorRefData& cursor); + virtual ~wxCursorRefData(); + + virtual bool IsOk() const + { + if ( m_hCursor != NULL ) + return true; +#if !wxMAC_USE_COCOA + if ( m_themeCursor != -1 ) + return true; +#endif + + return false; + } + +protected: +#if wxMAC_USE_COCOA + WX_NSCursor m_hCursor; +#else + WXHCURSOR m_hCursor; + bool m_disposeHandle; + bool m_releaseHandle; + bool m_isColorCursor; + long m_themeCursor; +#endif + + friend class wxCursor; + + DECLARE_NO_ASSIGN_CLASS(wxCursorRefData) +}; + +#define M_CURSORDATA wx_static_cast(wxCursorRefData*, m_refData) + +ClassicCursor gMacCursors[kwxCursorLast+1] = +{ + +{ +{0x0000, 0x03E0, 0x0630, 0x0808, 0x1004, 0x31C6, 0x2362, 0x2222, +0x2362, 0x31C6, 0x1004, 0x0808, 0x0630, 0x03E0, 0x0000, 0x0000}, +{0x0000, 0x03E0, 0x07F0, 0x0FF8, 0x1FFC, 0x3FFE, 0x3FFE, 0x3FFE, +0x3FFE, 0x3FFE, 0x1FFC, 0x0FF8, 0x07F0, 0x03E0, 0x0000, 0x0000}, +{0x0007, 0x0008} +}, + +{ +{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +{0x0000, 0x0000} +}, + +{ +{0x00F0, 0x0088, 0x0108, 0x0190, 0x0270, 0x0220, 0x0440, 0x0440, +0x0880, 0x0880, 0x1100, 0x1E00, 0x1C00, 0x1800, 0x1000, 0x0000}, +{0x00F0, 0x00F8, 0x01F8, 0x01F0, 0x03F0, 0x03E0, 0x07C0, 0x07C0, +0x0F80, 0x0F80, 0x1F00, 0x1E00, 0x1C00, 0x1800, 0x1000, 0x0000}, +{0x000E, 0x0003} +}, + +{ +{0x0000, 0x1E00, 0x2100, 0x4080, 0x4080, 0x4080, 0x4080, 0x2180, +0x1FC0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x0006, 0x0000}, +{0x3F00, 0x7F80, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0x7FC0, +0x3FE0, 0x1FF0, 0x00F8, 0x007C, 0x003E, 0x001F, 0x000F, 0x0007}, +{0x0004, 0x0004} +}, + +{ +{0x0000, 0x07E0, 0x1FF0, 0x3838, 0x3C0C, 0x6E0E, 0x6706, 0x6386, +0x61C6, 0x60E6, 0x7076, 0x303C, 0x1C1C, 0x0FF8, 0x07E0, 0x0000}, +{0x0540, 0x0FF0, 0x3FF8, 0x3C3C, 0x7E0E, 0xFF0F, 0x6F86, 0xE7C7, +0x63E6, 0xE1F7, 0x70FE, 0x707E, 0x3C3C, 0x1FFC, 0x0FF0, 0x0540}, +{0x0007, 0x0007} +}, + +{ +{0x0000, 0x0380, 0x0380, 0x0380, 0x0380, 0x0380, 0x0380, 0x0FE0, +0x1FF0, 0x1FF0, 0x0000, 0x1FF0, 0x1FF0, 0x1550, 0x1550, 0x1550}, +{0x07C0, 0x07C0, 0x07C0, 0x07C0, 0x07C0, 0x07C0, 0x0FE0, 0x1FF0, +0x3FF8, 0x3FF8, 0x3FF8, 0x3FF8, 0x3FF8, 0x3FF8, 0x3FF8, 0x3FF8}, +{0x000B, 0x0007} +}, + +{ +{0x00C0, 0x0140, 0x0640, 0x08C0, 0x3180, 0x47FE, 0x8001, 0x8001, +0x81FE, 0x8040, 0x01C0, 0x0040, 0x03C0, 0xC080, 0x3F80, 0x0000}, +{0x00C0, 0x01C0, 0x07C0, 0x0FC0, 0x3F80, 0x7FFE, 0xFFFF, 0xFFFF, +0xFFFE, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFF80, 0x3F80, 0x0000}, +{0x0006, 0x000F} +}, + +{ +{0x0100, 0x0280, 0x0260, 0x0310, 0x018C, 0x7FE3, 0x8000, 0x8000, +0x7F80, 0x0200, 0x0380, 0x0200, 0x03C0, 0x0107, 0x01F8, 0x0000}, +{0x0100, 0x0380, 0x03E0, 0x03F0, 0x01FC, 0x7FFF, 0xFFFF, 0xFFFF, +0xFFFF, 0x03FF, 0x03FF, 0x03FF, 0x03FF, 0x01FF, 0x01F8, 0x0000}, +{0x0006, 0x0000} +}, + +{ +{0x0000, 0x4078, 0x60FC, 0x71CE, 0x7986, 0x7C06, 0x7E0E, 0x7F1C, +0x7FB8, 0x7C30, 0x6C30, 0x4600, 0x0630, 0x0330, 0x0300, 0x0000}, +{0xC078, 0xE0FC, 0xF1FE, 0xFBFF, 0xFFCF, 0xFF8F, 0xFF1F, 0xFFBE, +0xFFFC, 0xFE78, 0xFF78, 0xEFF8, 0xCFF8, 0x87F8, 0x07F8, 0x0300}, +{0x0001, 0x0001} +}, + +{ +{0x0000, 0x0002, 0x0006, 0x000E, 0x001E, 0x003E, 0x007E, 0x00FE, +0x01FE, 0x003E, 0x0036, 0x0062, 0x0060, 0x00C0, 0x00C0, 0x0000}, +{0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, +0x03FF, 0x07FF, 0x007F, 0x00F7, 0x00F3, 0x01E1, 0x01E0, 0x01C0}, +{0x0001, 0x000E} +}, + +{ +{0x0000, 0x0080, 0x01C0, 0x03E0, 0x0080, 0x0080, 0x0080, 0x1FFC, +0x1FFC, 0x0080, 0x0080, 0x0080, 0x03E0, 0x01C0, 0x0080, 0x0000}, +{0x0080, 0x01C0, 0x03E0, 0x07F0, 0x0FF8, 0x01C0, 0x3FFE, 0x3FFE, +0x3FFE, 0x3FFE, 0x01C0, 0x0FF8, 0x07F0, 0x03E0, 0x01C0, 0x0080}, +{0x0007, 0x0008} +}, + +{ +{0x0000, 0x0080, 0x01C0, 0x03E0, 0x0080, 0x0888, 0x188C, 0x3FFE, +0x188C, 0x0888, 0x0080, 0x03E0, 0x01C0, 0x0080, 0x0000, 0x0000}, +{0x0080, 0x01C0, 0x03E0, 0x07F0, 0x0BE8, 0x1DDC, 0x3FFE, 0x7FFF, +0x3FFE, 0x1DDC, 0x0BE8, 0x07F0, 0x03E0, 0x01C0, 0x0080, 0x0000}, +{0x0007, 0x0008} +}, + +{ +{0x0000, 0x001E, 0x000E, 0x060E, 0x0712, 0x03A0, 0x01C0, 0x00E0, +0x0170, 0x1238, 0x1C18, 0x1C00, 0x1E00, 0x0000, 0x0000, 0x0000}, +{0x007F, 0x003F, 0x0E1F, 0x0F0F, 0x0F97, 0x07E3, 0x03E1, 0x21F0, +0x31F8, 0x3A7C, 0x3C3C, 0x3E1C, 0x3F00, 0x3F80, 0x0000, 0x0000}, +{0x0006, 0x0009} +}, + +{ +{0x0000, 0x7800, 0x7000, 0x7060, 0x48E0, 0x05C0, 0x0380, 0x0700, +0x0E80, 0x1C48, 0x1838, 0x0038, 0x0078, 0x0000, 0x0000, 0x0000}, +{0xFE00, 0xFC00, 0xF870, 0xF0F0, 0xE9F0, 0xC7E0, 0x87C0, 0x0F84, +0x1F8C, 0x3E5C, 0x3C3C, 0x387C, 0x00FC, 0x01FC, 0x0000, 0x0000}, +{0x0006, 0x0006} +}, + +{ +{0x0006, 0x000E, 0x001C, 0x0018, 0x0020, 0x0040, 0x00F8, 0x0004, +0x1FF4, 0x200C, 0x2AA8, 0x1FF0, 0x1F80, 0x3800, 0x6000, 0x8000}, +{0x000F, 0x001F, 0x003E, 0x007C, 0x0070, 0x00E0, 0x01FC, 0x3FF6, +0x7FF6, 0x7FFE, 0x7FFC, 0x7FF8, 0x3FF0, 0x7FC0, 0xF800, 0xE000}, +{0x000A, 0x0006} +}, + +}; + +wxCursor gMacCurrentCursor ; + +#if !wxMAC_USE_COCOA +CursHandle wxGetStockCursor( int number ) +{ + wxASSERT_MSG( number >= 0 && number <=kwxCursorLast , wxT("invalid stock cursor id") ) ; + CursHandle c = (CursHandle) NewHandle( sizeof(Cursor) ) ; + memcpy( *c, &gMacCursors[number], sizeof(Cursor) ) ; + +#ifndef WORDS_BIGENDIAN + short *sptr = (short*) *c ; + for ( int i = 0 ; i < 2 * 16 /* image and mask */ ; ++i, ++sptr ) + { + *sptr = CFSwapInt16( *sptr ) ; + } +#endif + return c ; +} +#endif + +wxCursorRefData::wxCursorRefData() +{ + m_hCursor = NULL; +#if wxMAC_USE_COCOA +#else + m_disposeHandle = false; + m_releaseHandle = false; + m_isColorCursor = false; + m_themeCursor = -1; +#endif +} + +wxCursorRefData::wxCursorRefData(const wxCursorRefData& cursor) +{ + // FIXME: need to copy the cursor + m_hCursor = NULL; + +#if wxMAC_USE_COCOA + wxUnusedVar(cursor); +#else + m_disposeHandle = false; + m_releaseHandle = false; + m_isColorCursor = cursor.m_isColorCursor; + m_themeCursor = cursor.m_themeCursor; +#endif +} + +wxCursorRefData::~wxCursorRefData() +{ +#if wxMAC_USE_COCOA + if ( m_hCursor ) + wxMacCocoaRelease(m_hCursor); +#else + if ( m_isColorCursor ) + { +#ifndef __LP64__ + ::DisposeCCursor( (CCrsrHandle) m_hCursor ) ; +#endif + } + else if ( m_disposeHandle ) + { + ::DisposeHandle( (Handle ) m_hCursor ) ; + } + else if ( m_releaseHandle ) + { + // we don't release the resource since it may already + // be in use again + } +#endif +} + +wxCursor::wxCursor() +{ +} + +wxCursor::wxCursor(const char WXUNUSED(bits)[], int WXUNUSED(width), int WXUNUSED(height), + int WXUNUSED(hotSpotX), int WXUNUSED(hotSpotY), const char WXUNUSED(maskBits)[]) +{ +} + +wxCursor::wxCursor( const wxImage &image ) +{ +#if wxUSE_IMAGE + CreateFromImage( image ) ; +#endif +} + +wxCursor::wxCursor(const char* const* bits) +{ + (void) CreateFromXpm(bits); +} + +wxGDIRefData *wxCursor::CreateGDIRefData() const +{ + return new wxCursorRefData; +} + +wxGDIRefData *wxCursor::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxCursorRefData(*wx_static_cast(const wxCursorRefData *, data)); +} + +bool wxCursor::CreateFromXpm(const char* const* bits) +{ +#if wxUSE_IMAGE + wxCHECK_MSG( bits != NULL, false, wxT("invalid cursor data") ); + wxXPMDecoder decoder; + wxImage img = decoder.ReadData(bits); + wxCHECK_MSG( img.Ok(), false, wxT("invalid cursor data") ); + CreateFromImage( img ) ; + return true; +#else + return false; +#endif +} + +WXHCURSOR wxCursor::GetHCURSOR() const +{ + return (M_CURSORDATA ? M_CURSORDATA->m_hCursor : 0); +} + +#if !wxMAC_USE_COCOA +short GetCTabIndex( CTabHandle colors , RGBColor *col ) +{ + short retval = 0 ; + unsigned long bestdiff = 0xFFFF ; + + for ( int i = 0 ; i < (**colors).ctSize ; ++i ) + { + unsigned long diff = abs(col->red - (**colors).ctTable[i].rgb.red ) + + abs(col->green - (**colors).ctTable[i].rgb.green ) + + abs(col->blue - (**colors).ctTable[i].rgb.blue ) ; + + if ( diff < bestdiff ) + { + bestdiff = diff ; + retval = (**colors).ctTable[i].value ; + } + } + + return retval ; +} +#endif + +#if wxUSE_IMAGE + +void wxCursor::CreateFromImage(const wxImage & image) +{ + m_refData = new wxCursorRefData; + int hotSpotX = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X); + int hotSpotY = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y); +#if wxMAC_USE_COCOA + wxBitmap bmp( image ); + CGImageRef cgimage = wxMacCreateCGImageFromBitmap(bmp); + if ( cgimage ) + { + M_CURSORDATA->m_hCursor = wxMacCocoaCreateCursorFromCGImage( cgimage, hotSpotX, hotSpotY ); + CFRelease( cgimage ); + } +#else +#ifndef __LP64__ + int w = 16; + int h = 16; + + int image_w = image.GetWidth(); + int image_h = image.GetHeight(); + + wxASSERT_MSG( hotSpotX >= 0 && hotSpotX < image_w && + hotSpotY >= 0 && hotSpotY < image_h, + wxT("invalid cursor hot spot coordinates") ); + + wxImage image16(image); // final image of correct size + + // if image is too small then place it in the center, resize it if too big + if ((w > image_w) && (h > image_h)) + { + wxPoint offset((w - image_w) / 2, (h - image_h) / 2); + hotSpotX = hotSpotX + offset.x; + hotSpotY = hotSpotY + offset.y; + + image16 = image.Size(wxSize(w, h), offset); + } + else if ((w != image_w) || (h != image_h)) + { + hotSpotX = int(hotSpotX * double(w) / double(image_w)); + hotSpotY = int(hotSpotY * double(h) / double(image_h)); + + image16 = image.Scale(w, h); + } + + unsigned char * rgbBits = image16.GetData(); + bool bHasMask = image16.HasMask() ; + + PixMapHandle pm = (PixMapHandle) NewHandleClear( sizeof(PixMap) ) ; + short extent = 16 ; + short bytesPerPixel = 1 ; + short depth = 8 ; + Rect bounds = { 0 , 0 , extent , extent } ; + CCrsrHandle ch = (CCrsrHandle) NewHandleClear( sizeof(CCrsr) ) ; + CTabHandle newColors = GetCTable( 8 ) ; + HandToHand( (Handle *) &newColors ); + + // set the values to the indices + for ( int i = 0 ; i < (**newColors).ctSize ; ++i ) + { + (**newColors).ctTable[i].value = i ; + } + + HLock( (Handle)ch ); + (**ch).crsrType = 0x8001; // color cursors + (**ch).crsrMap = pm; + short bytesPerRow = bytesPerPixel * extent; + + (**pm).baseAddr = 0; + (**pm).rowBytes = bytesPerRow | 0x8000; + (**pm).bounds = bounds; + (**pm).pmVersion = 0; + (**pm).packType = 0; + (**pm).packSize = 0; + (**pm).hRes = 0x00480000; // 72 DPI default res + (**pm).vRes = 0x00480000; // 72 DPI default res + (**pm).pixelSize = depth; + (**pm).pixelType = 0; + (**pm).cmpCount = 1; + (**pm).cmpSize = depth; + (**pm).pmTable = newColors; + + (**ch).crsrData = NewHandleClear( extent * bytesPerRow ) ; + (**ch).crsrXData = NULL ; + (**ch).crsrXValid = 0; + (**ch).crsrXHandle = NULL; + + (**ch).crsrHotSpot.h = hotSpotX ; + (**ch).crsrHotSpot.v = hotSpotY ; + (**ch).crsrXTable = 0 ; + (**ch).crsrID = GetCTSeed() ; + + memset( (**ch).crsr1Data , 0 , sizeof( Bits16 ) ) ; + memset( (**ch).crsrMask , 0 , sizeof( Bits16 ) ) ; + + unsigned char mr = image16.GetMaskRed() ; + unsigned char mg = image16.GetMaskGreen() ; + unsigned char mb = image16.GetMaskBlue() ; + + for ( int y = 0 ; y < h ; ++y ) + { + short rowbits = 0, maskbits = 0 ; + + for ( int x = 0 ; x < w ; ++x ) + { + long pos = (y * w + x) * 3; + + unsigned char r = rgbBits[pos] ; + unsigned char g = rgbBits[pos + 1] ; + unsigned char b = rgbBits[pos + 2] ; + RGBColor col = { 0xFFFF, 0xFFFF, 0xFFFF } ; + + if ( bHasMask && r == mr && g == mg && b == mb ) + { + // masked area, does not appear anywhere + } + else + { + if ( (int)r + (int)g + (int)b < 0x0200 ) + rowbits |= ( 1 << (15 - x) ) ; + + maskbits |= ( 1 << (15 - x) ) ; + + wxColor( r , g , b ).GetRGBColor( &col ); + } + + *((*(**ch).crsrData) + y * bytesPerRow + x) = + GetCTabIndex( newColors , &col) ; + } +#ifdef WORDS_BIGENDIAN + (**ch).crsr1Data[y] = rowbits ; + (**ch).crsrMask[y] = maskbits ; +#else + (**ch).crsr1Data[y] = CFSwapInt16(rowbits) ; + (**ch).crsrMask[y] = CFSwapInt16(maskbits) ; +#endif + } + + if ( !bHasMask ) + memcpy( (**ch).crsrMask , (**ch).crsr1Data , sizeof( Bits16) ) ; + + HUnlock( (Handle)ch ) ; + M_CURSORDATA->m_hCursor = ch ; + M_CURSORDATA->m_isColorCursor = true ; +#endif +#endif +} + +#endif //wxUSE_IMAGE + +wxCursor::wxCursor(const wxString& cursor_file, wxBitmapType flags, int hotSpotX, int hotSpotY) +{ + m_refData = new wxCursorRefData; + if ( flags == wxBITMAP_TYPE_MACCURSOR_RESOURCE ) + { +#if wxMAC_USE_COCOA + wxFAIL_MSG( wxT("Not implemented") ); +#else +#ifndef __LP64__ + Str255 theName ; + wxMacStringToPascal( cursor_file , theName ) ; + + Handle resHandle = ::GetNamedResource( 'crsr' , theName ) ; + if ( resHandle ) + { + short theId = -1 ; + OSType theType ; + + GetResInfo( resHandle , &theId , &theType , theName ) ; + ReleaseResource( resHandle ) ; + + M_CURSORDATA->m_hCursor = GetCCursor( theId ) ; + if ( M_CURSORDATA->m_hCursor ) + M_CURSORDATA->m_isColorCursor = true ; + } + else + { + Handle resHandle = ::GetNamedResource( 'CURS' , theName ) ; + if ( resHandle ) + { + short theId = -1 ; + OSType theType ; + + GetResInfo( resHandle , &theId , &theType , theName ) ; + ReleaseResource( resHandle ) ; + + M_CURSORDATA->m_hCursor = GetCursor( theId ) ; + if ( M_CURSORDATA->m_hCursor ) + M_CURSORDATA->m_releaseHandle = true ; + } + } +#endif +#endif + } + else + { +#if wxUSE_IMAGE + wxImage image ; + image.LoadFile( cursor_file, flags ) ; + if ( image.Ok() ) + { + image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, hotSpotX ) ; + image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, hotSpotY ) ; + m_refData->DecRef() ; + m_refData = NULL ; + CreateFromImage( image ) ; + } +#endif + } +} + +// Cursors by stock number +wxCursor::wxCursor(int cursor_type) +{ + m_refData = new wxCursorRefData; +#if wxMAC_USE_COCOA + M_CURSORDATA->m_hCursor = wxMacCocoaCreateStockCursor( cursor_type ); +#else + switch (cursor_type) + { + case wxCURSOR_COPY_ARROW: + M_CURSORDATA->m_themeCursor = kThemeCopyArrowCursor; + break; + + case wxCURSOR_WAIT: + M_CURSORDATA->m_themeCursor = kThemeWatchCursor; + break; + + case wxCURSOR_IBEAM: + M_CURSORDATA->m_themeCursor = kThemeIBeamCursor; + break; + + case wxCURSOR_CROSS: + M_CURSORDATA->m_themeCursor = kThemeCrossCursor; + break; + + case wxCURSOR_SIZENWSE: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSizeNWSE); + break; + + case wxCURSOR_SIZENESW: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSizeNESW); + break; + + case wxCURSOR_SIZEWE: + M_CURSORDATA->m_themeCursor = kThemeResizeLeftRightCursor; + break; + + case wxCURSOR_SIZENS: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSizeNS); + break; + + case wxCURSOR_SIZING: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorSize); + break; + + case wxCURSOR_HAND: + M_CURSORDATA->m_themeCursor = kThemePointingHandCursor; + break; + + case wxCURSOR_BULLSEYE: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorBullseye); + break; + + case wxCURSOR_PENCIL: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPencil); + break; + + case wxCURSOR_MAGNIFIER: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorMagnifier); + break; + + case wxCURSOR_NO_ENTRY: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorNoEntry); + break; + + case wxCURSOR_WATCH: + M_CURSORDATA->m_themeCursor = kThemeWatchCursor; + break; + + case wxCURSOR_PAINT_BRUSH: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPaintBrush); + break; + + case wxCURSOR_POINT_LEFT: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPointLeft); + break; + + case wxCURSOR_POINT_RIGHT: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorPointRight); + break; + + case wxCURSOR_QUESTION_ARROW: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorQuestionArrow); + break; + + case wxCURSOR_BLANK: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorBlank); + break; + + case wxCURSOR_RIGHT_ARROW: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorRightArrow); + break; + + case wxCURSOR_SPRAYCAN: + M_CURSORDATA->m_hCursor = wxGetStockCursor(kwxCursorRoller); + break; + + case wxCURSOR_CHAR: + case wxCURSOR_ARROW: + case wxCURSOR_LEFT_BUTTON: + case wxCURSOR_RIGHT_BUTTON: + case wxCURSOR_MIDDLE_BUTTON: + default: + M_CURSORDATA->m_themeCursor = kThemeArrowCursor; + break; + } + + if ( M_CURSORDATA->m_themeCursor == -1 ) + M_CURSORDATA->m_releaseHandle = true; +#endif +} + +void wxCursor::MacInstall() const +{ + gMacCurrentCursor = *this ; +#if wxMAC_USE_COCOA + if ( IsOk() ) + wxMacCocoaSetCursor( M_CURSORDATA->m_hCursor ); +#else + if ( m_refData && M_CURSORDATA->m_themeCursor != -1 ) + { + SetThemeCursor( M_CURSORDATA->m_themeCursor ) ; + } + else if ( m_refData && M_CURSORDATA->m_hCursor ) + { +#ifndef __LP64__ + if ( M_CURSORDATA->m_isColorCursor ) + ::SetCCursor( (CCrsrHandle) M_CURSORDATA->m_hCursor ) ; + else + ::SetCursor( * (CursHandle) M_CURSORDATA->m_hCursor ) ; +#endif + } + else + { + SetThemeCursor( kThemeArrowCursor ) ; + } +#endif +} + +wxCursor::~wxCursor() +{ +} + +// Global cursor setting +wxCursor gGlobalCursor; +void wxSetCursor(const wxCursor& cursor) +{ + cursor.MacInstall() ; + gGlobalCursor = cursor; +} diff --git a/src/osx/carbon/databrow.cpp b/src/osx/carbon/databrow.cpp new file mode 100644 index 0000000000..6d4e55cc5b --- /dev/null +++ b/src/osx/carbon/databrow.cpp @@ -0,0 +1,1145 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/databrow.cpp +// Purpose: Classes and functions for the Carbon data browser +// Author: +// Modified by: +// Created: 2007-05-18 +// RCS-ID: $Id$ +// Copyright: (c) +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_DATAVIEWCTRL +#ifndef wxUSE_GENERICDATAVIEWCTRL + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/toplevel.h" + #include "wx/font.h" + #include "wx/settings.h" + #include "wx/utils.h" +#endif + +#include "wx/dataview.h" +#include "wx/mac/carbon/databrow.h" +#include "wx/mac/private.h" +#include "wx/mac/uma.h" + +#include + +// ============================================================================ +// Variables used globally in databrow.cpp +// ============================================================================ +DataBrowserGetContextualMenuUPP gDataBrowserTableViewGetContextualMenuUPP = NULL; +DataBrowserItemCompareUPP gDataBrowserTableViewItemCompareUPP = NULL; +DataBrowserItemDataUPP gDataBrowserTableViewItemDataUPP = NULL; +DataBrowserItemNotificationUPP gDataBrowserTableViewItemNotificationUPP = NULL; + +DataBrowserDrawItemUPP gDataBrowserTableViewDrawItemUPP = NULL; +DataBrowserEditItemUPP gDataBrowserTableViewEditItemUPP = NULL; +DataBrowserHitTestUPP gDataBrowserTableViewHitTestUPP = NULL; +DataBrowserTrackingUPP gDataBrowserTableViewTrackingUPP = NULL; + +// ============================================================================ +// wxMacDataBrowserTableViewControl +// ============================================================================ + +pascal Boolean wxMacDataBrowserTableViewControl::DataBrowserCompareProc(ControlRef browser, DataBrowserItemID itemOneID, DataBrowserItemID itemTwoID, DataBrowserPropertyID sortProperty) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + if (ControlPtr != NULL) + return ControlPtr->DataBrowserCompareProc(itemOneID,itemTwoID,sortProperty); + else + return errDataBrowserPropertyNotSupported; +} /* wxMacDataBrowserTableViewControl::DataBrowserCompareProc(ControlRef, DataBrowserItemID, DataBrowserItemID, DataBrowserPropertyID) */ + +pascal void wxMacDataBrowserTableViewControl::DataBrowserGetContextualMenuProc(ControlRef browser, MenuRef* menu, UInt32* helpType, CFStringRef* helpItemString, AEDesc* selection) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + if (ControlPtr != NULL) + ControlPtr->DataBrowserGetContextualMenuProc(menu,helpType,helpItemString,selection); +} /* wxMacDataBrowserTableViewControl::DataBrowserGetContextualMenuProc(ControlRef, MenuRef*, UInt32*, CFStringRef*, AEDesc*) */ + +pascal OSStatus wxMacDataBrowserTableViewControl::DataBrowserGetSetItemDataProc(ControlRef browser, DataBrowserItemID itemID, DataBrowserPropertyID propertyID, DataBrowserItemDataRef itemData, Boolean getValue) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + if (ControlPtr != NULL) + return ControlPtr->DataBrowserGetSetItemDataProc(itemID,propertyID,itemData,getValue); + else + return errDataBrowserPropertyNotSupported; +} /* wxMacDataBrowserTableViewControl::DataBrowserGetSetItemDataProc(ControlRef, DataBrowserItemID, DataBrowserPropertyID, DataBrowserItemDataRef, Boolean) */ + +pascal void wxMacDataBrowserTableViewControl::DataBrowserItemNotificationProc(ControlRef browser, DataBrowserItemID itemID, DataBrowserItemNotification message, DataBrowserItemDataRef itemData) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + if (ControlPtr != NULL) + ControlPtr->DataBrowserItemNotificationProc(itemID,message,itemData); +} /* wxMacDataBrowserTableViewControl::DataBrowserItemNotificationProc(ControlRef, DataBrowserItemID, DataBrowserItemNotification, DataBrowserItemDataRef) */ + +pascal void wxMacDataBrowserTableViewControl::DataBrowserDrawItemProc(ControlRef browser, DataBrowserItemID itemID, DataBrowserPropertyID propertyID, DataBrowserItemState state, Rect const* rectangle, SInt16 bitDepth, Boolean colorDevice) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + if (ControlPtr != NULL) + ControlPtr->DataBrowserDrawItemProc(itemID,propertyID,state,rectangle,bitDepth,colorDevice); +} /* wxMacDataBrowserTableViewControl::DataBrowserDrawItemProc(ControlRef, DataBrowserItemID, DataBrowserPropertyID, DataBrowserItemState, Rect const*, SInt16, Boolean) */ + +pascal Boolean wxMacDataBrowserTableViewControl::DataBrowserEditItemProc(ControlRef browser, DataBrowserItemID itemID, DataBrowserPropertyID propertyID, CFStringRef theString, Rect* maxEditTextRect, Boolean* shrinkToFit) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + return ((ControlPtr != NULL) && ControlPtr->DataBrowserEditItemProc(itemID,propertyID,theString,maxEditTextRect,shrinkToFit)); +} /* wxMacDataBrowserTableViewControl::DataBrowserEditItemProc(ControlRef, DataBrowserItemID, DataBrowserPropertyID, CFStringRef, Rect*, Boolean*) */ + +pascal Boolean wxMacDataBrowserTableViewControl::DataBrowserHitTestProc(ControlRef browser, DataBrowserItemID itemID, DataBrowserPropertyID propertyID, Rect const* theRect, Rect const* mouseRect) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + return ((ControlPtr != NULL) && ControlPtr->DataBrowserHitTestProc(itemID,propertyID,theRect,mouseRect)); +} /* wxMacDataBrowserTableViewControl::DataBrowserHitTestProc(ControlRef, DataBrowserItemID, DataBrowserPropertyID, Rect const*, Rect const*) */ + +pascal DataBrowserTrackingResult wxMacDataBrowserTableViewControl::DataBrowserTrackingProc(ControlRef browser, DataBrowserItemID itemID, DataBrowserPropertyID propertyID, Rect const* theRect, Point startPt, EventModifiers modifiers) +{ + wxMacDataBrowserTableViewControl* ControlPtr(dynamic_cast(wxMacControl::GetReferenceFromNativeControl(browser))); + + + if (ControlPtr != NULL) + return ControlPtr->DataBrowserTrackingProc(itemID,propertyID,theRect,startPt,modifiers); + else + return kDataBrowserNothingHit; +} /* wxMacDataBrowserTableViewControl::DataBrowserTrackingProc(ControlRef, DataBrowserItemID, DataBrowserPropertyID, Rect const*, Point, EventModifiers) */ + +wxMacDataBrowserTableViewControl::wxMacDataBrowserTableViewControl(wxWindow* peer, wxPoint const& pos, wxSize const& size, long style) + :wxMacControl(peer) +{ + Rect bounds = wxMacGetBoundsForControl(peer,pos,size); + OSStatus err = ::CreateDataBrowserControl(MAC_WXHWND(peer->MacGetTopLevelWindowRef()),&bounds,kDataBrowserListView,&(this->m_controlRef)); + + + SetReferenceInNativeControl(); + verify_noerr(err); + // setup standard callbacks: + if (gDataBrowserTableViewGetContextualMenuUPP == NULL) gDataBrowserTableViewGetContextualMenuUPP = NewDataBrowserGetContextualMenuUPP(wxMacDataBrowserTableViewControl::DataBrowserGetContextualMenuProc); + if (gDataBrowserTableViewItemCompareUPP == NULL) gDataBrowserTableViewItemCompareUPP = NewDataBrowserItemCompareUPP (wxMacDataBrowserTableViewControl::DataBrowserCompareProc); + if (gDataBrowserTableViewItemDataUPP == NULL) gDataBrowserTableViewItemDataUPP = NewDataBrowserItemDataUPP (wxMacDataBrowserTableViewControl::DataBrowserGetSetItemDataProc); + if (gDataBrowserTableViewItemNotificationUPP == NULL) + { + gDataBrowserTableViewItemNotificationUPP = +#if TARGET_API_MAC_OSX + (DataBrowserItemNotificationUPP) NewDataBrowserItemNotificationWithItemUPP(wxMacDataBrowserTableViewControl::DataBrowserItemNotificationProc); +#else + NewDataBrowserItemNotificationUPP(wxMacDataBrowserTableViewControl::DataBrowserItemNotificationProc); +#endif + } + DataBrowserCallbacks callbacks; // variable definition + + InitializeDataBrowserCallbacks(&callbacks,kDataBrowserLatestCallbacks); + callbacks.u.v1.getContextualMenuCallback = gDataBrowserTableViewGetContextualMenuUPP; + callbacks.u.v1.itemDataCallback = gDataBrowserTableViewItemDataUPP; + callbacks.u.v1.itemCompareCallback = gDataBrowserTableViewItemCompareUPP; + callbacks.u.v1.itemNotificationCallback = gDataBrowserTableViewItemNotificationUPP; + this->SetCallbacks(&callbacks); + + // setup callbacks for customized items: + if (gDataBrowserTableViewDrawItemUPP == NULL) gDataBrowserTableViewDrawItemUPP = NewDataBrowserDrawItemUPP(wxMacDataBrowserTableViewControl::DataBrowserDrawItemProc); + if (gDataBrowserTableViewEditItemUPP == NULL) gDataBrowserTableViewEditItemUPP = NewDataBrowserEditItemUPP(wxMacDataBrowserTableViewControl::DataBrowserEditItemProc); + if (gDataBrowserTableViewHitTestUPP == NULL) gDataBrowserTableViewHitTestUPP = NewDataBrowserHitTestUPP (wxMacDataBrowserTableViewControl::DataBrowserHitTestProc); + if (gDataBrowserTableViewTrackingUPP == NULL) gDataBrowserTableViewTrackingUPP = NewDataBrowserTrackingUPP(wxMacDataBrowserTableViewControl::DataBrowserTrackingProc); + + DataBrowserCustomCallbacks customCallbacks; // variable definition + + InitializeDataBrowserCustomCallbacks(&customCallbacks,kDataBrowserLatestCallbacks); + customCallbacks.u.v1.drawItemCallback = gDataBrowserTableViewDrawItemUPP; + customCallbacks.u.v1.editTextCallback = gDataBrowserTableViewEditItemUPP; + customCallbacks.u.v1.hitTestCallback = gDataBrowserTableViewHitTestUPP; + customCallbacks.u.v1.trackingCallback = gDataBrowserTableViewTrackingUPP; + this->SetCustomCallbacks(&customCallbacks); + + // style setting: + this->EnableCellSizeModification( ((style & wxDV_VARIABLE_LINE_HEIGHT) != 0), true ); + + DataBrowserSelectionFlags flags; // variable definition + + if (this->GetSelectionFlags(&flags) == noErr) // get default settings + { + if ((style & wxDV_MULTIPLE) != 0) + flags &= ~kDataBrowserSelectOnlyOne; + else + flags |= kDataBrowserSelectOnlyOne; + (void) this->SetSelectionFlags(flags); + } /* if */ + + OptionBits attributes; // variable definition + + if (this->GetAttributes(&attributes) == noErr) // get default settings + { + if ((style & wxDV_VERT_RULES) != 0) + attributes |= kDataBrowserAttributeListViewDrawColumnDividers; + else + attributes &= ~kDataBrowserAttributeListViewDrawColumnDividers; + if ((style & wxDV_ROW_LINES) != 0) + attributes |= kDataBrowserAttributeListViewAlternatingRowColors; + else + attributes &= ~kDataBrowserAttributeListViewAlternatingRowColors; + (void) this->SetAttributes(attributes); + } /* if */ + + if ((style & wxDV_NO_HEADER) != 0) + this->SetHeaderButtonHeight(0); +} /* wxMacDataBrowserTableViewControl::wxMacDataBrowserTableViewControl(wxWindow*, wxPoint const&, wxSize const&, long) */ + +// +// callback handling +// +OSStatus wxMacDataBrowserTableViewControl::SetCallbacks(DataBrowserCallbacks const* callbacks) +{ + return ::SetDataBrowserCallbacks(this->m_controlRef,callbacks); +} /* wxMacDataBrowserTableViewControl::SetCallbacks(DataBrowserCallbacks const*) */ + +OSStatus wxMacDataBrowserTableViewControl::SetCustomCallbacks(DataBrowserCustomCallbacks const* customCallbacks) +{ + return ::SetDataBrowserCustomCallbacks(this->m_controlRef,customCallbacks); +} /* xMacDataBrowserTableViewControl::SetCustomCallbacks(DataBrowserCustomCallbacks const*) */ + +// +// header handling +// +OSStatus wxMacDataBrowserTableViewControl::GetHeaderDesc(DataBrowserPropertyID propertyID, DataBrowserListViewHeaderDesc* desc) const +{ + desc->version = kDataBrowserListViewLatestHeaderDesc; // if this statement is missing the next call will fail (NOT DOCUMENTED!!) + return ::GetDataBrowserListViewHeaderDesc(this->m_controlRef,propertyID,desc); +} + +OSStatus wxMacDataBrowserTableViewControl::SetHeaderDesc(DataBrowserPropertyID propertyID, DataBrowserListViewHeaderDesc* desc) +{ + return ::SetDataBrowserListViewHeaderDesc(this->m_controlRef,propertyID,desc); +} + +// +// layout handling +// +OSStatus wxMacDataBrowserTableViewControl::AutoSizeColumns() +{ + return AutoSizeDataBrowserListViewColumns(this->m_controlRef); +} + +OSStatus wxMacDataBrowserTableViewControl::EnableCellSizeModification(bool enableHeight, bool enableWidth) +{ + return ::SetDataBrowserTableViewGeometry(this->GetControlRef(),enableWidth,enableHeight); +} /* wxMacDataBrowserTableViewControl::EnableCellSizeModification(bool, bool) */ + +OSStatus wxMacDataBrowserTableViewControl::GetAttributes(OptionBits* attributes) +{ + return ::DataBrowserGetAttributes(this->GetControlRef(),attributes); +} /* wxMacDataBrowserTableViewControl::GetAttributes(OptionBits*) */ + +OSStatus wxMacDataBrowserTableViewControl::GetColumnWidth(DataBrowserPropertyID propertyID, UInt16* width) const +{ + return ::GetDataBrowserTableViewNamedColumnWidth(this->m_controlRef,propertyID,width); +} + +OSStatus wxMacDataBrowserTableViewControl::GetDefaultColumnWidth( UInt16 *width ) const +{ + return GetDataBrowserTableViewColumnWidth(this->m_controlRef, width ); +} + +OSStatus wxMacDataBrowserTableViewControl::GetDefaultRowHeight(UInt16* height) const +{ + return ::GetDataBrowserTableViewRowHeight(this->m_controlRef,height); +} + +OSStatus wxMacDataBrowserTableViewControl::GetHeaderButtonHeight(UInt16 *height) +{ + return ::GetDataBrowserListViewHeaderBtnHeight(this->m_controlRef,height); +} + +OSStatus wxMacDataBrowserTableViewControl::GetPartBounds(DataBrowserItemID item, DataBrowserPropertyID propertyID, DataBrowserPropertyPart part, Rect* bounds) +{ + return ::GetDataBrowserItemPartBounds(this->m_controlRef,item,propertyID,part,bounds); +} /* wxMacDataBrowserTableViewControl::GetPartBounds(DataBrowserItemID, DataBrowswerPropertyID, DataBrowserPropertyPart, Rect*) */ + +OSStatus wxMacDataBrowserTableViewControl::GetRowHeight(DataBrowserItemID item, UInt16* height) const +{ + return ::GetDataBrowserTableViewItemRowHeight(this->m_controlRef,item,height); +} /* wxMacDataBrowserTableViewControl::GetRowHeight(DataBrowserItemID, UInt16*) const */ + +OSStatus wxMacDataBrowserTableViewControl::GetScrollPosition( UInt32 *top , UInt32 *left ) const +{ + return GetDataBrowserScrollPosition(this->m_controlRef, top , left ); +} + +OSStatus wxMacDataBrowserTableViewControl::SetAttributes(OptionBits attributes) +{ + return ::DataBrowserChangeAttributes(this->GetControlRef(),attributes,~attributes); +} /* wxMacDataBrowserTableViewControl::SetAttributes(OptionBits) */ + +OSStatus wxMacDataBrowserTableViewControl::SetColumnWidth(DataBrowserPropertyID propertyID, UInt16 width) +{ + return ::SetDataBrowserTableViewNamedColumnWidth(this->m_controlRef,propertyID,width); +} + +OSStatus wxMacDataBrowserTableViewControl::SetDefaultColumnWidth(UInt16 width) +{ + return ::SetDataBrowserTableViewColumnWidth(this->m_controlRef,width); +} + +OSStatus wxMacDataBrowserTableViewControl::SetDefaultRowHeight(UInt16 height) +{ + return ::SetDataBrowserTableViewRowHeight(this->m_controlRef,height); +} + +OSStatus wxMacDataBrowserTableViewControl::SetHasScrollBars(bool horiz, bool vert) +{ + return ::SetDataBrowserHasScrollBars(this->m_controlRef,horiz,vert); +} /* wxMacDataBrowserTableViewControl::SetHasScrollBars(bool, bool) */ + +OSStatus wxMacDataBrowserTableViewControl::SetHeaderButtonHeight(UInt16 height) +{ + return ::SetDataBrowserListViewHeaderBtnHeight(this->m_controlRef,height); +} /* wxMacDataBrowserTableViewControl::SetHeaderButtonHeight(UInt16) */ + +OSStatus wxMacDataBrowserTableViewControl::SetHiliteStyle(DataBrowserTableViewHiliteStyle hiliteStyle) +{ + return ::SetDataBrowserTableViewHiliteStyle(this->m_controlRef,hiliteStyle); +} /*wxMacDataBrowserTableViewControl::SetHiliteStyle(DataBrowserTableViewHiliteStyle) */ + +OSStatus wxMacDataBrowserTableViewControl::SetIndent(float Indent) +{ + return ::DataBrowserSetMetric(this->m_controlRef,kDataBrowserMetricDisclosureColumnPerDepthGap,true,Indent); +} /* wxMacDataBrowserTableViewControl::SetIndent(float* Indent) */ + +OSStatus wxMacDataBrowserTableViewControl::SetRowHeight(DataBrowserItemID item, UInt16 height) +{ + return ::SetDataBrowserTableViewItemRowHeight(this->m_controlRef,item,height); +} + +OSStatus wxMacDataBrowserTableViewControl::SetScrollPosition(UInt32 top, UInt32 left) +{ + return ::SetDataBrowserScrollPosition(this->m_controlRef,top,left); +} + +// +// column handling +// +OSStatus wxMacDataBrowserTableViewControl::GetColumnCount(UInt32* numColumns) const +{ + return ::GetDataBrowserTableViewColumnCount(this->m_controlRef,numColumns); +} + +OSStatus wxMacDataBrowserTableViewControl::GetColumnIndex(DataBrowserPropertyID propertyID, DataBrowserTableViewColumnIndex* index) const +{ + return ::GetDataBrowserTableViewColumnPosition(this->m_controlRef,propertyID,index); +} /* wxMacDataBrowserTableViewControl::GetColumnIndex(DataBrowserPropertyID, DataBrowserTableViewColumnIndex*) const */ + +OSStatus wxMacDataBrowserTableViewControl::GetFreePropertyID(DataBrowserPropertyID* propertyID) const +{ + for (*propertyID=kMinPropertyID; *propertyID::max(); ++(*propertyID)) + if (this->IsUsedPropertyID(*propertyID) == errDataBrowserPropertyNotFound) + return noErr; + return errDataBrowserPropertyNotSupported; +} /* wxMacDataBrowserTableViewControl::GetFreePropertyID(DataBrowserPropertyID*) const */ + +OSStatus wxMacDataBrowserTableViewControl::GetPropertyFlags(DataBrowserPropertyID propertyID, DataBrowserPropertyFlags *flags) const +{ + return ::GetDataBrowserPropertyFlags(this->m_controlRef,propertyID,flags); +} + +OSStatus wxMacDataBrowserTableViewControl::GetPropertyID(DataBrowserItemDataRef itemData, DataBrowserPropertyID* propertyID) +{ + return ::GetDataBrowserItemDataProperty(itemData,propertyID); +} /* wxMacDataBrowserTableViewControl::GetPropertyID(DataBrowserItemDataRef, DataBrowserPropertyID*) */ + +OSStatus wxMacDataBrowserTableViewControl::GetPropertyID(DataBrowserTableViewColumnIndex index, DataBrowserTableViewColumnID* propertyID) +{ + return ::GetDataBrowserTableViewColumnProperty(this->m_controlRef,index,propertyID); +} /* wxMacDataBrowserTableViewControl::GetPropertyID(DataBrowserTableViewColumnIndex, DataBrowserTableViewColumnID*) */ + +OSStatus wxMacDataBrowserTableViewControl::IsUsedPropertyID(DataBrowserPropertyID propertyID) const +{ + // as the Mac interface does not provide a function that checks if the property id is in use or not a function is chosen that should not + // lead to a large overhead for the call but returns an error code if the property id does not exist, here we use the function that returns + // the column position for the property id: + DataBrowserTableViewColumnIndex index; + + return ::GetDataBrowserTableViewColumnPosition(this->m_controlRef,propertyID,&index); +} /* wxMacDataBrowserTableViewControl::IsUsedPropertyId(DataBrowserPropertyID) const */ + +OSStatus wxMacDataBrowserTableViewControl::RemoveColumnByProperty(DataBrowserTableViewColumnID propertyID) +{ + return ::RemoveDataBrowserTableViewColumn(this->m_controlRef,propertyID); +} /* wxMacDataBrowserTableViewControl::RemoveColumnByProperty(DataBrowserTableViewColumnID) */ + +OSStatus wxMacDataBrowserTableViewControl::RemoveColumnByIndex(DataBrowserTableViewColumnIndex index) +{ + DataBrowserTableViewColumnID propertyID; + + + this->GetPropertyID(index,&propertyID); + return ::RemoveDataBrowserTableViewColumn(this->m_controlRef,propertyID); +} /* wxMacDataBrowserTableViewControl::RemoveColumnByIndex(DataBrowserTableViewColumnIndex) */ + +OSStatus wxMacDataBrowserTableViewControl::SetColumnIndex(DataBrowserPropertyID propertyID, DataBrowserTableViewColumnIndex index) +{ + return ::SetDataBrowserTableViewColumnPosition(this->m_controlRef,propertyID,index); +} /* wxMacDataBrowserTableViewControl::SetColumnIndex(DataBrowserPropertyID, DataBrowserTableViewColumnIndex) */ + +OSStatus wxMacDataBrowserTableViewControl::SetDisclosureColumn(DataBrowserPropertyID propertyID, Boolean expandableRows) +{ + return ::SetDataBrowserListViewDisclosureColumn(this->m_controlRef,propertyID,expandableRows); +} + +OSStatus wxMacDataBrowserTableViewControl::SetPropertyFlags(DataBrowserPropertyID propertyID, DataBrowserPropertyFlags flags) +{ + return ::SetDataBrowserPropertyFlags(this->m_controlRef,propertyID,flags); +} /* wxMacDataBrowserTableViewControl::SetPropertyFlags(DataBrowserPropertyID, DataBrowserPropertyFlags) */ + +// +// item handling +// +OSStatus wxMacDataBrowserTableViewControl::AddItems(DataBrowserItemID container, UInt32 numItems, DataBrowserItemID const* items, DataBrowserPropertyID preSortProperty) +{ + return ::AddDataBrowserItems(this->m_controlRef,container,numItems,items,preSortProperty); +} /* wxMacDataBrowserTableViewControl::AddItems(DataBrowserItemID, UInt32, DataBrowserItemID const*, DataBrowserPropertyID) */ + +OSStatus wxMacDataBrowserTableViewControl::GetFreeItemID(DataBrowserItemID* id) const +{ + ItemCount NoOfItems; + + OSStatus status; + + + status = this->GetItemCount(&NoOfItems); + wxCHECK_MSG(status == noErr,status,_("Could not retrieve number of items")); + if (NoOfItems == 0) + { + *id = 1; + return noErr; + } /* if */ + else + { + // as there might be a lot of items in the data browser and mostly the data is added item by item the largest used ID number is roughly in the order of magnitude + // as the number of items; therefore, start from the number of items to look for a new ID: + for (*id=NoOfItems; *id::max(); ++(*id)) + if (this->IsUsedItemID(*id) == errDataBrowserItemNotFound) + return noErr; + // as the first approach was not successful, try from the beginning: + for (*id=0; *idIsUsedItemID(*id) == errDataBrowserItemNotFound) + return noErr; + // sorry, data browser is full: + return errDataBrowserItemNotAdded; + } /* if */ +} /* wxMacDataBrowserTableViewControl::GetFreeItemID(DataBrowserItemID*) const */ + +OSStatus wxMacDataBrowserTableViewControl::GetItemCount(DataBrowserItemID container, Boolean recurse, DataBrowserItemState state, ItemCount* numItems) const +{ + return GetDataBrowserItemCount(this->m_controlRef,container,recurse,state,numItems); +} /* wxMacDataBrowserTableViewControl::GetItemCount(DataBrowserItemID, Boolean, DataBrowserItemState, ItemCount*) const */ + +OSStatus wxMacDataBrowserTableViewControl::GetItemID( DataBrowserTableViewRowIndex row, DataBrowserItemID * item ) const +{ + return GetDataBrowserTableViewItemID(this->m_controlRef,row,item); +} + +OSStatus wxMacDataBrowserTableViewControl::GetItems(DataBrowserItemID container, Boolean recurse, DataBrowserItemState state, Handle items) const +{ + return GetDataBrowserItems(this->m_controlRef,container,recurse,state,items); +} /* wxMacDataBrowserTableViewControl::GetItems(DataBrowserItemID, Boolean, DataBrowserItemState, Handle) const */ + +OSStatus wxMacDataBrowserTableViewControl::GetItemRow(DataBrowserItemID item, DataBrowserTableViewRowIndex* row) const +{ + return GetDataBrowserTableViewItemRow(this->m_controlRef,item,row); +} + +OSStatus wxMacDataBrowserTableViewControl::IsUsedItemID(DataBrowserItemID itemID) const +{ + // as the Mac interface does not provide a function that checks if the property id is in use or not a function is chosen that should not + // lead to a large overhead for the call but returns an error code if the property id does not exist, here we use the function that returns + // the column position for the property id: + DataBrowserTableViewColumnIndex index; + + return ::GetDataBrowserTableViewItemRow(this->m_controlRef,itemID,&index); +} /* wxMacDataBrowserTableViewControl::IsUsedItemID(DataBrowserItemID) const */ + +OSStatus wxMacDataBrowserTableViewControl::RemoveItems(DataBrowserItemID container, UInt32 numItems, DataBrowserItemID const* items, DataBrowserPropertyID preSortProperty) +{ + return ::RemoveDataBrowserItems(this->m_controlRef,container,numItems,items,preSortProperty); +} + +OSStatus wxMacDataBrowserTableViewControl::RevealItem(DataBrowserItemID item, DataBrowserPropertyID propertyID, DataBrowserRevealOptions options) const +{ + return ::RevealDataBrowserItem(this->m_controlRef,item,propertyID,options); +} /* wxMacDataBrowserTableViewControl::RevealItem(DataBrowserItemID item, DataBrowserPropertyID propertyID, DataBrowserRevealOptions options) const */ + +OSStatus wxMacDataBrowserTableViewControl::UpdateItems(DataBrowserItemID container, UInt32 numItems, DataBrowserItemID const* items, DataBrowserPropertyID preSortProperty, + DataBrowserPropertyID propertyID) const +{ + return UpdateDataBrowserItems(this->m_controlRef,container,numItems,items,preSortProperty,propertyID); +} + +// +// item selection +// +size_t wxMacDataBrowserTableViewControl::GetSelectedItemIDs(wxArrayDataBrowserItemID& itemIDs) const +{ + DataBrowserItemID* itemIDPtr; + Handle handle(::NewHandle(0)); + + size_t NoOfItems; + + + wxCHECK_MSG(this->GetItems(kDataBrowserNoItem,true,kDataBrowserItemIsSelected,handle) == noErr,0,_("Could not get selected items.")); + NoOfItems = static_cast(::GetHandleSize(handle)/sizeof(DataBrowserItemID)); + itemIDs.Empty(); + itemIDs.Alloc(NoOfItems); + HLock(handle); + itemIDPtr = (DataBrowserItemID*) (*handle); + for (size_t i=0; im_controlRef,first,last); +} /* wxMacDataBrowserTableViewControl::GetSelectionAnchor(DataBrowserItemID*, DataBrowserItemID*) const */ + +OSStatus wxMacDataBrowserTableViewControl::GetSelectionFlags(DataBrowserSelectionFlags* flags) const +{ + return ::GetDataBrowserSelectionFlags(this->m_controlRef,flags); +} /* wxMacDataBrowserTableViewControl::GetSelectionFlags(DataBrowserSelectionFlags*) const */ + +bool wxMacDataBrowserTableViewControl::IsItemSelected(DataBrowserItemID item) const +{ + return ::IsDataBrowserItemSelected(this->m_controlRef,item); +} /* wxMacDataBrowserTableViewControl::IsItemSelected(DataBrowserItemID) const */ + +OSStatus wxMacDataBrowserTableViewControl::SetSelectionFlags(DataBrowserSelectionFlags flags) +{ + return ::SetDataBrowserSelectionFlags(this->m_controlRef,flags); +} /* wxMacDataBrowserTableViewControl::SetSelectionFlags(DataBrowserSelectionFlags) */ + +OSStatus wxMacDataBrowserTableViewControl::SetSelectedItems(UInt32 numItems, DataBrowserItemID const* items, DataBrowserSetOption operation) +{ + return ::SetDataBrowserSelectedItems(this->m_controlRef, numItems, items, operation ); +} /* wxMacDataBrowserTableViewControl::SetSelectedItems(UInt32, DataBrowserItemID const*, DataBrowserSetOption) */ + +OSStatus wxMacDataBrowserTableViewControl::GetSortOrder(DataBrowserSortOrder* order) const +{ + return ::GetDataBrowserSortOrder(this->m_controlRef,order); +} + +OSStatus wxMacDataBrowserTableViewControl::GetSortProperty(DataBrowserPropertyID* propertyID) const +{ + return ::GetDataBrowserSortProperty(this->m_controlRef,propertyID); +} + +OSStatus wxMacDataBrowserTableViewControl::Resort(DataBrowserItemID container, Boolean sortChildren) +{ + return ::SortDataBrowserContainer(this->m_controlRef,container,sortChildren); +} /* wxMacDataBrowserTableViewControl::Resort(DataBrowserItemID, Boolean) */ + +OSStatus wxMacDataBrowserTableViewControl::SetSortOrder(DataBrowserSortOrder order) +{ + return ::SetDataBrowserSortOrder(this->m_controlRef,order); +} + +OSStatus wxMacDataBrowserTableViewControl::SetSortProperty(DataBrowserPropertyID propertyID) +{ + return ::SetDataBrowserSortProperty(this->m_controlRef,propertyID); +} + +// +// container handling +// +OSStatus wxMacDataBrowserTableViewControl::CloseContainer(DataBrowserItemID containerID) +{ + return ::CloseDataBrowserContainer(this->m_controlRef,containerID); +} /* wxMacDataBrowserTableViewControl::CloseContainer(DataBrowserItemID) */ + +OSStatus wxMacDataBrowserTableViewControl::OpenContainer(DataBrowserItemID containerID) +{ + return ::OpenDataBrowserContainer(this->m_controlRef,containerID); +} /* wxMacDataBrowserTableViewControl::OpenContainer(DataBrowserItemID) */ + +IMPLEMENT_ABSTRACT_CLASS(wxMacDataBrowserTableViewControl,wxMacControl) + +// ============================================================================ +// wxMacDataBrowserListViewControl +// ============================================================================ +#pragma mark - +// +// column handling +// +OSStatus wxMacDataBrowserListViewControl::AddColumn(DataBrowserListViewColumnDesc *columnDesc, DataBrowserTableViewColumnIndex position) +{ + return AddDataBrowserListViewColumn(this->m_controlRef,columnDesc,position); +} /* wxMacDataBrowserListViewControl::AddColumn(DataBrowserListViewColumnDesc*, DataBrowserTableViewColumnIndex) */ + +// ============================================================================ +// wxMacDataViewDataBrowserListViewControl +// ============================================================================ +#pragma mark - +// +// constructors / destructor +// +wxMacDataViewDataBrowserListViewControl::wxMacDataViewDataBrowserListViewControl(wxWindow* peer, wxPoint const& pos, wxSize const& size, long style) + :wxMacDataBrowserListViewControl(peer,pos,size,style) +{ +} /* wxMacDataViewDataBrowserListViewControl::wxMacDataViewDataBrowserListViewControl(wxWindow* , const wxPoint&, const wxSize&, long) */ + +// +// callback functions (inherited from wxMacDataBrowserTableViewControl) +// +Boolean wxMacDataViewDataBrowserListViewControl::DataBrowserCompareProc(DataBrowserItemID itemOneID, DataBrowserItemID itemTwoID, DataBrowserPropertyID sortProperty) +{ + DataBrowserSortOrder sortOrder; + + DataBrowserTableViewColumnIndex modelColumnIndex; + + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->GetPeer())); + + + wxCHECK_MSG(dataViewCtrlPtr != NULL, false,_("Pointer to data view control not set correctly.")); + wxCHECK_MSG(dataViewCtrlPtr->GetModel() != NULL,false,_("Pointer to model not set correctly.")); + if (sortProperty >= kMinPropertyID) + { + // variable definition and initialization: + wxDataViewColumn* ColumnPtr(dataViewCtrlPtr->GetColumnPtr(sortProperty)); + + wxCHECK_MSG(ColumnPtr != NULL,false,_("Could not determine column index.")); + modelColumnIndex = ColumnPtr->GetModelColumn(); + } /* if */ + else + modelColumnIndex = 0; + this->GetSortOrder(&sortOrder); + return static_cast(dataViewCtrlPtr->GetModel()->Compare(wxDataViewItem(reinterpret_cast(itemOneID)),wxDataViewItem(reinterpret_cast(itemTwoID)), + modelColumnIndex,sortOrder != kDataBrowserOrderDecreasing) < 0); +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserCompareProc(DataBrowserItemID, DataBrowserItemID, DataBrowserPropertyID) */ + +void wxMacDataViewDataBrowserListViewControl::DataBrowserGetContextualMenuProc(MenuRef* menu, UInt32* helpType, CFStringRef* helpItemString, AEDesc* WXUNUSED(selection)) + // In this method we do not supply a contextual menu handler at all but only send a wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU. +{ + wxArrayDataBrowserItemID itemIDs; + + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->GetPeer())); + + + wxCHECK_RET(dataViewCtrlPtr != NULL,_("wxWidget control pointer is not a data view pointer")); + // initialize parameters so that no context menu will be displayed automatically by the native data browser: + *menu = NULL; + *helpType = kCMHelpItemNoHelp; + *helpItemString = NULL; + // create information for a context menu event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_CONTEXT_MENU,dataViewCtrlPtr->GetId()); + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetModel(dataViewCtrlPtr->GetModel()); + // get the item information; + // theoretically more than one ID can be returned but the event can only handle one item, therefore all item related data is using the data of the first item in the array: + if (this->GetSelectedItemIDs(itemIDs) > 0) + dataViewEvent.SetItem(wxDataViewItem(reinterpret_cast(itemIDs[0]))); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserGetContextualMenuProc(MenuRef*, UInt32*, CFStringRef*, AEDesc*) */ + +OSStatus wxMacDataViewDataBrowserListViewControl::DataBrowserGetSetItemDataProc(DataBrowserItemID itemID, DataBrowserPropertyID propertyID, DataBrowserItemDataRef itemData, Boolean getValue) +{ + if (getValue) + { + // variable definitions: + wxDataViewCtrl* dataViewCtrlPtr; + + dataViewCtrlPtr = dynamic_cast(this->GetPeer()); + wxCHECK_MSG(dataViewCtrlPtr != NULL,errDataBrowserNotConfigured,_("Pointer to data view control not set correctly.")); + if (dataViewCtrlPtr->IsDeleting()) + return noErr; // if a delete process is running the data of editable fields cannot be saved because the associated model variable may already have been deleted + else + { + // variable definitions: + OSStatus errorStatus; + wxDataViewColumn* dataViewColumnPtr; + + wxCHECK_MSG(dataViewCtrlPtr->GetModel() != NULL,errDataBrowserNotConfigured,_("Pointer to model not set correctly.")); + dataViewColumnPtr = dataViewCtrlPtr->GetColumnPtr(propertyID); + wxCHECK_MSG((dataViewColumnPtr != NULL) && (dataViewColumnPtr->GetRenderer() != NULL),errDataBrowserNotConfigured,_("There is no column or renderer for the specified column index.")); + + wxDataViewItem dvItem(reinterpret_cast(itemID)); + unsigned int col = dataViewColumnPtr->GetModelColumn(); + + switch (dataViewColumnPtr->GetRenderer()->GetPropertyType()) + { + case kDataBrowserCheckboxType: + { + // variable definition: + ThemeButtonValue buttonValue; + + errorStatus = ::GetDataBrowserItemDataButtonValue(itemData,&buttonValue); + if (errorStatus == noErr) + { + if (buttonValue == kThemeButtonOn) + { + // variable definition and initialization: + wxVariant modifiedData(true); + + if (dataViewCtrlPtr->GetModel()->SetValue(modifiedData, dvItem, col) && + dataViewCtrlPtr->GetModel()->ValueChanged(dvItem, col)) + return noErr; + else + return errDataBrowserInvalidPropertyData; + } /* if */ + else if (buttonValue == kThemeButtonOff) + { + // variable definition and initialization: + wxVariant modifiedData(false); + + if (dataViewCtrlPtr->GetModel()->SetValue(modifiedData, dvItem, col) && + dataViewCtrlPtr->GetModel()->ValueChanged(dvItem, col)) + return noErr; + else + return errDataBrowserInvalidPropertyData; + } /* if */ + else + return errDataBrowserInvalidPropertyData; + } /* if */ + else + return errorStatus; + } /* block */ + case kDataBrowserTextType: + { + // variable definitions: + CFStringRef stringReference; + + errorStatus = ::GetDataBrowserItemDataText(itemData,&stringReference); + if (errorStatus == noErr) + { + // variable definitions and initializations: +#if wxCHECK_VERSION(2,9,0) + wxCFStringRef modifiedString(stringReference); +#else + wxMacCFStringHolder modifiedString(stringReference); +#endif + wxVariant modifiedData(modifiedString.AsString()); + + if (dataViewCtrlPtr->GetModel()->SetValue(modifiedData, dvItem, col) && + dataViewCtrlPtr->GetModel()->ValueChanged(dvItem, col)) + return noErr; + else + return errDataBrowserInvalidPropertyData; + } /* if */ + else + return errorStatus; + } /* block */ + default: + return errDataBrowserPropertyNotSupported; + } /* switch */ + } /* if */ + } /* if */ + else + { + if (propertyID >= kMinPropertyID) // in case data columns set the data + { + // variable definitions: + wxVariant variant; + wxDataViewColumn* dataViewColumnPtr; + wxDataViewCtrl* dataViewCtrlPtr; + + dataViewCtrlPtr = dynamic_cast(this->GetPeer()); + wxCHECK_MSG(dataViewCtrlPtr != NULL,errDataBrowserNotConfigured,_("Pointer to data view control not set correctly.")); + wxCHECK_MSG(dataViewCtrlPtr->GetModel() != NULL,errDataBrowserNotConfigured,_("Pointer to model not set correctly.")); + dataViewColumnPtr = dataViewCtrlPtr->GetColumnPtr(propertyID); + wxCHECK_MSG(dataViewColumnPtr != NULL,errDataBrowserNotConfigured,_("No column for the specified column position existing.")); + wxCHECK_MSG(dataViewColumnPtr->GetRenderer() != NULL,errDataBrowserNotConfigured,_("No renderer specified for column.")); + dataViewCtrlPtr->GetModel()->GetValue(variant,wxDataViewItem(reinterpret_cast(itemID)),dataViewColumnPtr->GetModelColumn()); + if (!(variant.IsNull())) + { + dataViewColumnPtr->GetRenderer()->SetDataReference(itemData); + dataViewColumnPtr->GetRenderer()->SetValue(variant); + wxCHECK_MSG(dataViewColumnPtr->GetRenderer()->Render(),errDataBrowserNotConfigured,_("Rendering failed.")); + } /* if */ + return noErr; + } /* if */ + else // react on special system requests + { + switch (propertyID) + { + case kDataBrowserContainerIsClosableProperty: + { + // variable definitions: + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->GetPeer())); + + wxCHECK_MSG(dataViewCtrlPtr != NULL,errDataBrowserNotConfigured,_("Pointer to data view control not set correctly.")); + // initialize wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSING,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem (wxDataViewItem(reinterpret_cast(itemID))); + dataViewEvent.SetModel (dataViewCtrlPtr->GetModel()); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + // opening the container is allowed if not vetoed: + return ::SetDataBrowserItemDataBooleanValue(itemData,dataViewEvent.IsAllowed()); + } /* block */ + case kDataBrowserContainerIsOpenableProperty: + { + // variable definitions: + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->GetPeer())); + + wxCHECK_MSG(dataViewCtrlPtr != NULL,errDataBrowserNotConfigured,_("Pointer to data view control not set correctly.")); + // initialize wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDING,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem (wxDataViewItem(reinterpret_cast(itemID))); + dataViewEvent.SetModel (dataViewCtrlPtr->GetModel()); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + // opening the container is allowed if not vetoed: + return ::SetDataBrowserItemDataBooleanValue(itemData,dataViewEvent.IsAllowed()); + } /* block */ + case kDataBrowserItemIsContainerProperty: + { + // variable definition: + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->GetPeer())); + + wxCHECK_MSG(dataViewCtrlPtr != NULL,errDataBrowserNotConfigured,_("Pointer to data view control not set correctly.")); + wxCHECK_MSG(dataViewCtrlPtr->GetModel() != NULL,errDataBrowserNotConfigured,_("Pointer to model not set correctly.")); + return ::SetDataBrowserItemDataBooleanValue(itemData,dataViewCtrlPtr->GetModel()->IsContainer(wxDataViewItem(reinterpret_cast(itemID)))); + } /* block */ + case kDataBrowserItemIsEditableProperty: + return ::SetDataBrowserItemDataBooleanValue(itemData,true); + } /* switch */ + } /* if */ + } /* if */ + return errDataBrowserPropertyNotSupported; +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserGetSetItemDataProc(DataBrowserItemID, DataBrowserPropertyID, DataBrowserItemDataRef, Boolean) */ + +void wxMacDataViewDataBrowserListViewControl::DataBrowserItemNotificationProc(DataBrowserItemID itemID, DataBrowserItemNotification message, DataBrowserItemDataRef itemData) +{ + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->GetPeer())); + + + // check if the data view control pointer still exists because this call back function can still be called when the control has already been deleted: + if (dataViewCtrlPtr != NULL) + switch (message) + { + case kDataBrowserContainerClosed: + dataViewCtrlPtr->FinishCustomItemEditing(); // stop editing of a custom item first (if necessary) + { + // initialize wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_COLLAPSED,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem(wxDataViewItem(reinterpret_cast(itemID))); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + } /* block */ + break; + case kDataBrowserContainerOpened: + dataViewCtrlPtr->FinishCustomItemEditing(); // stop editing of a custom item first (if necessary) + { + // initialize wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EXPANDED,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem(wxDataViewItem(reinterpret_cast(itemID))); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + // add children to the expanded container: + dataViewCtrlPtr->AddChildrenLevel(wxDataViewItem(reinterpret_cast(itemID))); + } /* block */ + break; + case kDataBrowserEditStarted: + dataViewCtrlPtr->FinishCustomItemEditing(); // stop editing of a custom item first (if necessary) + { + // initialize wxWidget event: + DataBrowserPropertyID propertyID; + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_STARTED,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem(wxDataViewItem(reinterpret_cast(itemID))); + if (this->GetPropertyID(itemData,&propertyID) == noErr) + { + // variable definition and initialization: + DataBrowserTableViewColumnIndex columnIndex; + + wxCHECK_RET(this->GetColumnIndex(propertyID,&columnIndex),_("Column index not found.")); + dataViewEvent.SetColumn(columnIndex); + dataViewEvent.SetDataViewColumn(dataViewCtrlPtr->GetColumnPtr(propertyID)); + } /* if */ + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + } /* block */ + break; + case kDataBrowserEditStopped: + { + // initialize wxWidget event: + DataBrowserPropertyID propertyID; + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem(wxDataViewItem(reinterpret_cast(itemID))); + if (this->GetPropertyID(itemData,&propertyID) == noErr) + { + // variable definition and initialization: + DataBrowserTableViewColumnIndex columnIndex; + + wxCHECK_RET(this->GetColumnIndex(propertyID,&columnIndex),_("Column index not found.")); + dataViewEvent.SetColumn(columnIndex); + dataViewEvent.SetDataViewColumn(dataViewCtrlPtr->GetColumnPtr(propertyID)); + } /* if */ + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + } /* block */ + break; + case kDataBrowserItemAdded: + dataViewCtrlPtr->FinishCustomItemEditing(); + break; + case kDataBrowserItemDeselected: + dataViewCtrlPtr->FinishCustomItemEditing(); + break; + case kDataBrowserItemDoubleClicked: + { + // initialize wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem(wxDataViewItem(reinterpret_cast(itemID))); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + } /* block */ + break; + case kDataBrowserItemRemoved: + dataViewCtrlPtr->FinishCustomItemEditing(); // stop editing of a custom item first (if necessary) + break; + case kDataBrowserItemSelected: + break; // not implemented by wxWidgets; see kDataBrowserSelectionSetChanged + case kDataBrowserSelectionSetChanged: + { + // initialize wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED,dataViewCtrlPtr->GetId()); // variable definition + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetModel (dataViewCtrlPtr->GetModel()); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + } /* block */ + break; + case kDataBrowserTargetChanged: // no idea if this notification is ever sent + break; + case kDataBrowserUserStateChanged: + { + // finish custom item editing if necessary: + dataViewCtrlPtr->FinishCustomItemEditing(); + // update column widths: + for (size_t i=0; iGetColumnCount(); ++i) + { + // constant definition for abbreviational purposes: + wxDataViewColumn* const columnPtr = dataViewCtrlPtr->GetColumn(i); + // variable definition: + UInt16 columnWidth; + + wxCHECK_RET(this->GetColumnWidth(columnPtr->GetPropertyID(),&columnWidth) == noErr,_("Column width could not be determined")); + columnPtr->SetWidthVariable(columnWidth); + } /* for */ + // update sorting orders: + DataBrowserPropertyID propertyID; // variable definition + + if ((this->GetSortProperty(&propertyID) == noErr) && (propertyID >= kMinPropertyID)) + { + DataBrowserSortOrder sortOrder; + DataBrowserTableViewColumnIndex columnIndex; + + if ((this->GetSortOrder(&sortOrder) == noErr) && (this->GetColumnIndex(propertyID,&columnIndex) == noErr)) + { + // variable definition and initialization: + wxDataViewColumn* columnPtr; + columnPtr = dataViewCtrlPtr->GetColumn(columnIndex); + // check if the sort order has changed: + if ( columnPtr->IsSortOrderAscending() && (sortOrder == kDataBrowserOrderDecreasing) || + !(columnPtr->IsSortOrderAscending()) && (sortOrder == kDataBrowserOrderIncreasing)) + { + columnPtr->SetSortOrder(!(columnPtr->IsSortOrderAscending())); + // initialize wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED,dataViewCtrlPtr->GetId()); // variable defintion + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetColumn(columnIndex); + dataViewEvent.SetDataViewColumn(columnPtr); + // finally send the equivalent wxWidget event: + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); + } /* if */ + } /* if */ + } /* if */ + } /* block */ + break; + } /* switch */ +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserItemNotificationProc(DataBrowserItemID, DataBrowserItemNotification, DataBrowserItemDataRef) */ + +void wxMacDataViewDataBrowserListViewControl::DataBrowserDrawItemProc(DataBrowserItemID itemID, DataBrowserPropertyID propertyID, DataBrowserItemState state, Rect const* rectangle, SInt16 bitDepth, Boolean colorDevice) +{ + DataBrowserTableViewColumnIndex columnIndex; + + wxDataViewColumn* dataViewColumnPtr; + + wxDataViewCtrl* dataViewCtrlPtr; + + wxDataViewCustomRenderer* dataViewCustomRendererPtr; + + wxVariant dataToRender; + + dataViewCtrlPtr = dynamic_cast(this->GetPeer()); + wxCHECK_RET(dataViewCtrlPtr != NULL, _("Pointer to data view control not set correctly.")); + wxCHECK_RET(dataViewCtrlPtr->GetModel() != NULL,_("Pointer to model not set correctly.")); + wxCHECK_RET(this->GetColumnIndex(propertyID,&columnIndex) == noErr,_("Could not determine column index.")); + dataViewColumnPtr = dataViewCtrlPtr->GetColumnPtr(propertyID); + wxCHECK_RET(dataViewColumnPtr != NULL,_("No column for the specified column index existing.")); + dataViewCustomRendererPtr = dynamic_cast(dataViewColumnPtr->GetRenderer()); + wxCHECK_RET(dataViewCustomRendererPtr != NULL,_("No renderer or invalid renderer type specified for custom data column.")); + dataViewCtrlPtr->GetModel()->GetValue(dataToRender,wxDataViewItem(reinterpret_cast(itemID)),dataViewColumnPtr->GetModelColumn()); + dataViewCustomRendererPtr->SetValue(dataToRender); + + wxDataViewItem dataitem( reinterpret_cast(itemID) ); + dataViewCtrlPtr->GetModel()->GetValue(dataToRender,dataitem,dataViewColumnPtr->GetModelColumn()); + dataViewCustomRendererPtr->SetValue(dataToRender); + + // try to determine the content's size (drawable part): + Rect content; + RgnHandle rgn(NewRgn()); + UInt16 headerHeight; + + if (this->GetRegion(kControlContentMetaPart,rgn) == noErr) + GetRegionBounds(rgn,&content); + else + this->GetRect(&content); + ::DisposeRgn(rgn); + // space for the header + this->GetHeaderButtonHeight(&headerHeight); + content.top += headerHeight; + // extra space for the frame (todo: do not how to determine the space automatically from the control) + content.top += 5; + content.left += 5; + content.right -= 3; + content.bottom -= 3; + // extra space for the scrollbars: + content.bottom -= wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y); + content.right -= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + + wxDC *dc = dataViewCustomRendererPtr->GetDC(); + + wxRect cellrect( static_cast(rectangle->left), + static_cast(rectangle->top+2), + static_cast(1+rectangle->right-rectangle->left), + static_cast(rectangle->bottom-rectangle->top) ); + + bool is_active = IsControlActive( this->m_controlRef ); + if (state == kDataBrowserItemIsSelected) + { + + wxColour col( wxMacCreateCGColorFromHITheme( (is_active) ? + kThemeBrushAlternatePrimaryHighlightColor + : kThemeBrushSecondaryHighlightColor ) ); + + wxRect rect = cellrect; + Rect itemrect; + GetDataBrowserItemPartBounds( this->m_controlRef, itemID, propertyID, + kDataBrowserPropertyEnclosingPart, &itemrect ); + rect.x = itemrect.left; + rect.width = itemrect.right-itemrect.left+1; + + wxBrush selBrush( col ); + wxPen oldpen( dc->GetPen() ); + wxBrush oldbrush( dc->GetBrush() ); + dc->SetPen( *wxTRANSPARENT_PEN ); + dc->SetBrush( selBrush ); + dc->DrawRectangle(rect); + dc->SetBrush( oldbrush ); + dc->SetPen( oldpen ); + } + + wxDataViewModel *model = dataViewCtrlPtr->GetModel(); + if ((columnIndex == 0) || !model->IsContainer(dataitem) || model->HasContainerColumns(dataitem)) + { + // make sure that 'Render' can draw only in the allowed area: + dc->SetClippingRegion(content.left,content.top,content.right-content.left+1,content.bottom-content.top+1); + (void) (dataViewCustomRendererPtr->Render( cellrect, dc, + ((state == kDataBrowserItemIsSelected) ? wxDATAVIEW_CELL_SELECTED : 0))); + dc->DestroyClippingRegion(); // probably not necessary + } + + dataViewCustomRendererPtr->SetDC(NULL); +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserDrawItemProc(DataBrowserItemID, DataBrowserPropertyID, DataBrowserItemState, Rect const*, SInt16, Boolean) */ + +Boolean wxMacDataViewDataBrowserListViewControl::DataBrowserEditItemProc(DataBrowserItemID itemID, DataBrowserPropertyID propertyID, CFStringRef theString, Rect* maxEditTextRect, Boolean* shrinkToFit) +{ + return false; +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserEditItemProc(DataBrowserItemID, DataBrowserPropertyID, CFStringRef, Rect*, Boolean*) */ + +Boolean wxMacDataViewDataBrowserListViewControl::DataBrowserHitTestProc(DataBrowserItemID WXUNUSED(itemID), DataBrowserPropertyID WXUNUSED(property), Rect const* WXUNUSED(theRect), Rect const* WXUNUSED(mouseRect)) +{ + return true; +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserHitTestProc(DataBrowserItemID, DataBrowserPropertyID, Rect const*, Rect const*) */ + +DataBrowserTrackingResult wxMacDataViewDataBrowserListViewControl::DataBrowserTrackingProc(DataBrowserItemID itemID, DataBrowserPropertyID propertyID, Rect const* theRect, Point WXUNUSED(startPt), EventModifiers WXUNUSED(modifiers)) +{ + wxDataViewColumn* dataViewColumnPtr; + + wxDataViewCtrl* dataViewCtrlPtr; + + wxDataViewCustomRenderer* dataViewCustomRendererPtr; + + wxDataViewItem dataViewCustomRendererItem; + + + dataViewCustomRendererItem = reinterpret_cast(itemID); + wxCHECK_MSG(dataViewCustomRendererItem.IsOk(),kDataBrowserNothingHit,_("Invalid data view item")); + dataViewCtrlPtr = dynamic_cast(this->GetPeer()); + wxCHECK_MSG(dataViewCtrlPtr != NULL,kDataBrowserNothingHit,_("Pointer to data view control not set correctly.")); + dataViewColumnPtr = dataViewCtrlPtr->GetColumnPtr(propertyID); + wxCHECK_MSG(dataViewColumnPtr != NULL,kDataBrowserNothingHit,_("No column existing.")); + dataViewCustomRendererPtr = dynamic_cast(dataViewColumnPtr->GetRenderer()); + wxCHECK_MSG(dataViewCustomRendererPtr != NULL,kDataBrowserNothingHit,_("No renderer or invalid renderer type specified for custom data column.")); + // if the currently edited item is identical to the to be edited nothing is done (this hit should only be handled in the control itself): + if (dataViewCtrlPtr->GetCustomRendererItem() == dataViewCustomRendererItem) + return kDataBrowserContentHit; + // an(other) item is going to be edited and therefore the current editing - if existing - has to be finished: + if (dataViewCtrlPtr->GetCustomRendererPtr() != NULL) + { + dataViewCtrlPtr->GetCustomRendererPtr()->FinishEditing(); + dataViewCtrlPtr->SetCustomRendererItem(wxDataViewItem()); + dataViewCtrlPtr->SetCustomRendererPtr (NULL); + } /* if */ + // check if renderer has got a valid editor control for editing; if this is the case start editing of the new item: + if (dataViewCustomRendererPtr->HasEditorCtrl()) + { + // variable definition: + wxRect wxRectangle; + + ::wxMacNativeToRect(theRect,&wxRectangle); + dataViewCustomRendererPtr->StartEditing(dataViewCustomRendererItem,wxRectangle); + dataViewCtrlPtr->SetCustomRendererItem(dataViewCustomRendererItem); + dataViewCtrlPtr->SetCustomRendererPtr (dataViewCustomRendererPtr); + } /* if */ + return kDataBrowserContentHit; +} /* wxMacDataViewDataBrowserListViewControl::DataBrowserTrackingProc(DataBrowserItemID, DataBrowserPropertyID, Rect const*, Point, EventModifiers) */ + +#endif // wxUSE_GENERICDATAVIEWCTRL +#endif // wxUSE_DATAVIEWCTRL diff --git a/src/osx/carbon/dataobj.cpp b/src/osx/carbon/dataobj.cpp new file mode 100644 index 0000000000..cdec68b05d --- /dev/null +++ b/src/osx/carbon/dataobj.cpp @@ -0,0 +1,753 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dataobj.cpp +// Purpose: implementation of wxDataObject class +// Author: Stefan Csomor +// Modified by: +// Created: 10/21/99 +// RCS-ID: $Id$ +// Copyright: (c) 1999 Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_DATAOBJ + +#include "wx/dataobj.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/dcmemory.h" + #include "wx/image.h" +#endif + +#include "wx/mstream.h" +#include "wx/metafile.h" +#include "wx/tokenzr.h" + +#include "wx/mac/uma.h" + +#ifdef __DARWIN__ + #include +#endif + + +// ---------------------------------------------------------------------------- +// wxDataFormat +// ---------------------------------------------------------------------------- + +wxDataFormat::wxDataFormat() +{ + m_type = wxDF_INVALID; + m_format = 0; +} + +wxDataFormat::wxDataFormat( wxDataFormatId vType ) +{ + m_format = 0; + m_type = wxDF_INVALID; + SetType( vType ); +} + +wxDataFormat::wxDataFormat( const wxChar *zId ) +{ + m_format = 0; + m_type = wxDF_INVALID; + SetId( zId ); +} + +wxDataFormat::wxDataFormat( const wxString& rId ) +{ + m_format = 0; + m_type = wxDF_INVALID; + SetId( rId ); +} + +wxDataFormat::wxDataFormat(const wxDataFormat& rFormat) +{ + if ( rFormat.m_format ) + m_format = (NativeFormat) CFStringCreateCopy(NULL, (CFStringRef)rFormat.m_format); + else + m_format = 0; + m_type = rFormat.m_type; + m_id = rFormat.m_id; +} + +wxDataFormat::wxDataFormat( NativeFormat vFormat ) +{ + m_format = 0; + m_type = wxDF_INVALID; + SetId( vFormat ); +} + +wxDataFormat::~wxDataFormat() +{ + if ( m_format != 0 ) + { + CFRelease( (CFStringRef) m_format ); + m_format = 0; + } +} + +// in order to be correct for 10.3 we restrict to the available types there +// http://developer.apple.com/qa/qa2005/qa1406.html +// TODO : Use UTCoreTypes.h constants once we support 10.4+ only + +wxDataFormat& wxDataFormat::operator=(const wxDataFormat& rFormat) +{ + if ( m_format != 0 ) + { + CFRelease( (CFStringRef) m_format ); + m_format = 0; + } + if ( rFormat.m_format ) + m_format = (NativeFormat) CFStringCreateCopy(NULL, (CFStringRef)rFormat.m_format); + m_type = rFormat.m_type; + m_id = rFormat.m_id; + return *this; +} + +void wxDataFormat::SetType( wxDataFormatId dataType ) +{ + m_type = dataType; + if ( m_format != 0 ) + { + CFRelease( (CFStringRef) m_format ); + m_format = 0; + } + + switch (m_type) + { + case wxDF_TEXT: + m_format = (long) CFStringCreateCopy( NULL, CFSTR("public.plain-text") ); + break; + + case wxDF_UNICODETEXT: + m_format = (long) CFStringCreateCopy( NULL, CFSTR("public.utf16-plain-text") ); + break; + + case wxDF_BITMAP: + m_format = (long) CFStringCreateCopy( NULL, CFSTR("public.tiff") ); + break; + case wxDF_METAFILE: + m_format = (long) CFStringCreateCopy( NULL, CFSTR("com.adobe.pdf") ); + break; + + case wxDF_FILENAME: + m_format = (long) CFStringCreateCopy( NULL, CFSTR("public.file-url") ); + break; + + default: + wxFAIL_MSG( wxT("invalid data format") ); + break; + } +} + +wxString wxDataFormat::GetId() const +{ + return wxCFStringRef(wxCFRetain((CFStringRef)m_format)).AsString(); +} + +void wxDataFormat::SetId( NativeFormat format ) +{ + if ( m_format != 0 ) + { + CFRelease( (CFStringRef) m_format ); + m_format = 0; + } + m_format = (NativeFormat) CFStringCreateCopy(NULL, (CFStringRef)format); + if ( UTTypeConformsTo( (CFStringRef)format, CFSTR("public.utf16-plain-text") ) ) + { + m_type = wxDF_UNICODETEXT; + } + else if ( UTTypeConformsTo( (CFStringRef)format, CFSTR("public.plain-text") ) ) + { + m_type = wxDF_TEXT; + } + else if ( UTTypeConformsTo( (CFStringRef)format, CFSTR("public.tiff") ) ) + { + m_type = wxDF_BITMAP; + } + else if ( UTTypeConformsTo( (CFStringRef)format, CFSTR("com.adobe.pdf") ) ) + { + m_type = wxDF_METAFILE; + } + else if ( UTTypeConformsTo( (CFStringRef)format, CFSTR("public.file-url") ) ) + { + m_type = wxDF_FILENAME; + } + else + { + m_type = wxDF_PRIVATE; + m_id = wxCFStringRef( (CFStringRef) CFRetain((CFStringRef) format )).AsString(); + } +} + +void wxDataFormat::SetId( const wxString& zId ) +{ + m_type = wxDF_PRIVATE; + m_id = zId; + if ( m_format != 0 ) + { + CFRelease( (CFStringRef) m_format ); + m_format = 0; + } + // since it is private, no need to conform to anything ... + m_format = (long) wxCFRetain( (CFStringRef) wxCFStringRef(m_id) ); +} + +bool wxDataFormat::operator==(const wxDataFormat& format) const +{ + if (IsStandard() || format.IsStandard()) + return (format.m_type == m_type); + else + return ( UTTypeConformsTo( (CFStringRef) m_format , (CFStringRef) format.m_format ) ); +} + +//------------------------------------------------------------------------- +// wxDataObject +//------------------------------------------------------------------------- + +wxDataObject::wxDataObject() +{ +} + +bool wxDataObject::IsSupportedFormat( const wxDataFormat& rFormat, Direction vDir ) const +{ + size_t nFormatCount = GetFormatCount( vDir ); + bool found = false; + + if (nFormatCount == 1) + { + found = (rFormat == GetPreferredFormat()); + } + else + { + wxDataFormat *pFormats = new wxDataFormat[nFormatCount]; + GetAllFormats( pFormats, vDir ); + + for (size_t n = 0; n < nFormatCount; n++) + { + if (pFormats[n] == rFormat) + { + found = true; + break; + } + } + + delete [] pFormats; + } + + return found; +} + +void wxDataObject::AddToPasteboard( void * pb, int itemID ) +{ + PasteboardRef pasteboard = (PasteboardRef) pb; + // get formats from wxDataObjects + wxDataFormat *array = new wxDataFormat[ GetFormatCount() ]; + GetAllFormats( array ); + + for (size_t i = 0; i < GetFormatCount(); i++) + { + wxDataFormat thisFormat = array[ i ]; + + // add four bytes at the end for data objs like text that + // have a datasize = strlen but still need a buffer for the + // string including trailing zero + + size_t datasize = GetDataSize( thisFormat ); + size_t sz = datasize + 4; + void* buf = malloc( sz ); + if ( buf != NULL ) + { + // empty the buffer because in some case GetDataHere does not fill buf + memset( buf, 0, sz ); + if ( GetDataHere( array[ i ], buf ) ) + { + int counter = 1 ; + if ( thisFormat.GetType() == wxDF_FILENAME ) + { + // the data is D-normalized UTF8 strings of filenames delimited with \n + char *fname = strtok((char*) buf,"\n"); + while (fname != NULL) + { + // translate the filepath into a fileurl and put that into the pasteobard + CFStringRef path = CFStringCreateWithBytes(NULL,(UInt8*)fname,strlen(fname),kCFStringEncodingUTF8,false); + CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path , kCFURLPOSIXPathStyle, false); + CFRelease(path); + CFDataRef data = CFURLCreateData(NULL,url,kCFStringEncodingUTF8,true); + CFRelease(url); + PasteboardPutItemFlavor( pasteboard, (PasteboardItemID) counter, + (CFStringRef) thisFormat.GetFormatId() , data, kPasteboardFlavorNoFlags); + CFRelease( data ); + counter++; + fname = strtok (NULL,"\n"); + } + + } + else + { + CFDataRef data = CFDataCreate( kCFAllocatorDefault, (UInt8*)buf, datasize ); + if ( thisFormat.GetType() == wxDF_TEXT ) + PasteboardPutItemFlavor( pasteboard, (PasteboardItemID) itemID, + CFSTR("com.apple.traditional-mac-plain-text") , data, kPasteboardFlavorNoFlags); + else + PasteboardPutItemFlavor( pasteboard, (PasteboardItemID) itemID, + (CFStringRef) thisFormat.GetFormatId() , data, kPasteboardFlavorNoFlags); + CFRelease( data ); + } + } + free( buf ); + } + } + + delete [] array; +} + +bool wxDataObject::IsFormatInPasteboard( void * pb, const wxDataFormat &dataFormat ) +{ + PasteboardRef pasteboard = (PasteboardRef) pb; + bool hasData = false; + OSStatus err = noErr; + ItemCount itemCount; + + // we synchronize here once again, so we don't mind which flags get returned + PasteboardSynchronize( pasteboard ); + + err = PasteboardGetItemCount( pasteboard, &itemCount ); + if ( err == noErr ) + { + for( UInt32 itemIndex = 1; itemIndex <= itemCount && hasData == false ; itemIndex++ ) + { + PasteboardItemID itemID; + CFArrayRef flavorTypeArray; + CFIndex flavorCount; + + err = PasteboardGetItemIdentifier( pasteboard, itemIndex, &itemID ); + if ( err != noErr ) + continue; + + err = PasteboardCopyItemFlavors( pasteboard, itemID, &flavorTypeArray ); + if ( err != noErr ) + continue; + + flavorCount = CFArrayGetCount( flavorTypeArray ); + + for( CFIndex flavorIndex = 0; flavorIndex < flavorCount && hasData == false ; flavorIndex++ ) + { + CFStringRef flavorType; + + flavorType = (CFStringRef)CFArrayGetValueAtIndex( flavorTypeArray, + flavorIndex ); + + wxDataFormat flavorFormat( (wxDataFormat::NativeFormat) flavorType ); + if ( dataFormat == flavorFormat ) + hasData = true; + else if ( dataFormat.GetType() == wxDF_UNICODETEXT && flavorFormat.GetType() == wxDF_TEXT ) + hasData = true; + } + CFRelease (flavorTypeArray); + } + } + + return hasData; +} + +bool wxDataObject::GetFromPasteboard( void * pb ) +{ + PasteboardRef pasteboard = (PasteboardRef) pb; + size_t formatcount = GetFormatCount() + 1; + wxDataFormat *array = new wxDataFormat[ formatcount ]; + array[0] = GetPreferredFormat(); + GetAllFormats( &array[1] ); + ItemCount itemCount = 0; + wxString filenamesPassed; + bool transferred = false; + + // we synchronize here once again, so we don't mind which flags get returned + PasteboardSynchronize( pasteboard ); + + OSStatus err = PasteboardGetItemCount( pasteboard, &itemCount ); + if ( err == noErr ) + { + for (size_t i = 0; !transferred && i < formatcount; i++) + { + // go through the data in our order of preference + wxDataFormat dataFormat = array[ i ]; + + for( UInt32 itemIndex = 1; itemIndex <= itemCount && transferred == false ; itemIndex++ ) + { + PasteboardItemID itemID = 0; + CFArrayRef flavorTypeArray = NULL; + CFIndex flavorCount = 0; + + err = PasteboardGetItemIdentifier( pasteboard, itemIndex, &itemID ); + if ( err != noErr ) + continue; + + err = PasteboardCopyItemFlavors( pasteboard, itemID, &flavorTypeArray ); + if ( err != noErr ) + continue; + + flavorCount = CFArrayGetCount( flavorTypeArray ); + + for( CFIndex flavorIndex = 0; !transferred && flavorIndex < flavorCount ; flavorIndex++ ) + { + CFStringRef flavorType; + CFDataRef flavorData; + CFIndex flavorDataSize; + + flavorType = (CFStringRef)CFArrayGetValueAtIndex( flavorTypeArray, + flavorIndex ); + + wxDataFormat flavorFormat( (wxDataFormat::NativeFormat) flavorType ); + + if ( dataFormat == flavorFormat ) + { + err = PasteboardCopyItemFlavorData( pasteboard, itemID, flavorType , &flavorData ); + if ( err == noErr ) + { + flavorDataSize = CFDataGetLength( flavorData ); + if (dataFormat.GetType() == wxDF_FILENAME ) + { + // revert the translation and decomposition to arrive at a proper utf8 string again + CFURLRef url = CFURLCreateWithBytes( kCFAllocatorDefault, CFDataGetBytePtr( flavorData ), flavorDataSize, kCFStringEncodingUTF8, NULL ); + CFStringRef cfString = CFURLCopyFileSystemPath( url, kCFURLPOSIXPathStyle ); + CFRelease( url ); + CFMutableStringRef cfMutableString = CFStringCreateMutableCopy(NULL, 0, cfString); + CFRelease( cfString ); + CFStringNormalize(cfMutableString,kCFStringNormalizationFormC); + wxString path = wxCFStringRef(cfMutableString).AsString(); + if (!path.empty()) + filenamesPassed += path + wxT("\n"); + } + else + { + // because some data implementation expect trailing a trailing NUL, we add some headroom + void *buf = malloc( flavorDataSize + 4 ); + if ( buf ) + { + memset( buf, 0, flavorDataSize + 4 ); + memcpy( buf, CFDataGetBytePtr( flavorData ), flavorDataSize ); + + if (dataFormat.GetType() == wxDF_TEXT) + wxMacConvertNewlines10To13( (char*) buf ); + SetData( flavorFormat, flavorDataSize, buf ); + transferred = true; + free( buf ); + } + } + CFRelease (flavorData); + } + } + else if ( dataFormat.GetType() == wxDF_UNICODETEXT && flavorFormat.GetType() == wxDF_TEXT ) + { + err = PasteboardCopyItemFlavorData( pasteboard, itemID, flavorType, &flavorData ); + if ( err == noErr ) + { + flavorDataSize = CFDataGetLength( flavorData ); + void *asciibuf = malloc( flavorDataSize + 1 ); + if ( asciibuf ) + { + memset( asciibuf, 0, flavorDataSize + 1 ); + memcpy( asciibuf, CFDataGetBytePtr( flavorData ), flavorDataSize ); + CFRelease (flavorData); + + SetData( wxDF_TEXT, flavorDataSize, asciibuf ); + transferred = true; + free( asciibuf ); + } + else + CFRelease (flavorData); + } + } + } + CFRelease( flavorTypeArray ); + } + if (filenamesPassed.length() > 0) + { + wxCharBuffer buf = filenamesPassed.fn_str(); + SetData( wxDF_FILENAME, strlen( buf ), (const char*)buf ); + transferred = true; + } + } + } + return transferred; +} + +bool wxDataObject::HasDataInPasteboard( void * pb ) +{ + PasteboardRef pasteboard = (PasteboardRef) pb; + size_t formatcount = GetFormatCount() + 1; + wxDataFormat *array = new wxDataFormat[ formatcount ]; + array[0] = GetPreferredFormat(); + GetAllFormats( &array[1] ); + ItemCount itemCount = 0; + bool hasData = false; + + // we synchronize here once again, so we don't mind which flags get returned + PasteboardSynchronize( pasteboard ); + + OSStatus err = PasteboardGetItemCount( pasteboard, &itemCount ); + if ( err == noErr ) + { + for (size_t i = 0; !hasData && i < formatcount; i++) + { + // go through the data in our order of preference + wxDataFormat dataFormat = array[ i ]; + + for( UInt32 itemIndex = 1; itemIndex <= itemCount && hasData == false ; itemIndex++ ) + { + PasteboardItemID itemID = 0; + CFArrayRef flavorTypeArray = NULL; + CFIndex flavorCount = 0; + + err = PasteboardGetItemIdentifier( pasteboard, itemIndex, &itemID ); + if ( err != noErr ) + continue; + + err = PasteboardCopyItemFlavors( pasteboard, itemID, &flavorTypeArray ); + if ( err != noErr ) + continue; + + flavorCount = CFArrayGetCount( flavorTypeArray ); + + for( CFIndex flavorIndex = 0; !hasData && flavorIndex < flavorCount ; flavorIndex++ ) + { + CFStringRef flavorType; + + flavorType = (CFStringRef)CFArrayGetValueAtIndex( flavorTypeArray, + flavorIndex ); + + wxDataFormat flavorFormat( (wxDataFormat::NativeFormat) flavorType ); + + if ( dataFormat == flavorFormat || + dataFormat.GetType() == wxDF_UNICODETEXT && flavorFormat.GetType() == wxDF_TEXT ) + { + hasData = true; + } + } + CFRelease( flavorTypeArray ); + } + } + } + return hasData; +} + +// ---------------------------------------------------------------------------- +// wxTextDataObject +// ---------------------------------------------------------------------------- + +#if wxUSE_UNICODE +void wxTextDataObject::GetAllFormats(wxDataFormat *formats, + wxDataObjectBase::Direction WXUNUSED(dir)) const +{ + *formats++ = wxDataFormat( wxDF_TEXT ); + *formats = wxDataFormat( wxDF_UNICODETEXT ); +} +#endif + +// ---------------------------------------------------------------------------- +// wxFileDataObject +// ---------------------------------------------------------------------------- + +void wxFileDataObject::GetFileNames( wxCharBuffer &buf ) const +{ + wxString filenames; + + for (size_t i = 0; i < m_filenames.GetCount(); i++) + { + filenames += m_filenames[i]; + filenames += wxT('\n'); + } + + buf = filenames.fn_str(); +} + +bool wxFileDataObject::GetDataHere( void *pBuf ) const +{ + if (pBuf == NULL) + return false; + + wxCharBuffer buf; + size_t buffLength; + + GetFileNames( buf ); + buffLength = strlen( buf ); + memcpy( pBuf, (const char*)buf, buffLength + 1 ); + + return true; +} + +size_t wxFileDataObject::GetDataSize() const +{ + wxCharBuffer buf; + size_t buffLength; + + GetFileNames( buf ); + buffLength = strlen( buf ); + // terminating 0 + return buffLength + 1; +} + +bool wxFileDataObject::SetData( size_t WXUNUSED(nSize), const void *pBuf ) +{ + wxString filenames; + +#if wxUSE_UNICODE + filenames = wxString( (const char*)pBuf, *wxConvFileName ); +#else + filenames = wxString (wxConvLocal.cWC2WX(wxConvFileName->cMB2WC( (const char*)pBuf))); +#endif + + m_filenames = wxStringTokenize( filenames, wxT("\n"), wxTOKEN_STRTOK ); + + return true; +} + +void wxFileDataObject::AddFile( const wxString& rFilename ) +{ + m_filenames.Add( rFilename ); +} + +// ---------------------------------------------------------------------------- +// wxBitmapDataObject +// ---------------------------------------------------------------------------- + +wxBitmapDataObject::wxBitmapDataObject() +{ + Init(); +} + +wxBitmapDataObject::wxBitmapDataObject( const wxBitmap& rBitmap ) +: wxBitmapDataObjectBase( rBitmap ) +{ + Init(); + + if (m_bitmap.Ok()) + { + SetBitmap( rBitmap ); + } +} + +wxBitmapDataObject::~wxBitmapDataObject() +{ + Clear(); +} + +void wxBitmapDataObject::SetBitmap( const wxBitmap& rBitmap ) +{ + Clear(); + wxBitmapDataObjectBase::SetBitmap( rBitmap ); + if (m_bitmap.Ok()) + { + CGImageRef cgImageRef = (CGImageRef) m_bitmap.CreateCGImage(); + + CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); + CGImageDestinationRef destination = CGImageDestinationCreateWithData( data , kUTTypeTIFF , 1 , NULL ); + if ( destination ) + { + CGImageDestinationAddImage( destination, cgImageRef, NULL ); + CGImageDestinationFinalize( destination ); + CFRelease( destination ); + } + m_pictHandle = NewHandle(CFDataGetLength(data)); + if ( m_pictHandle ) + { + memcpy( *(Handle)m_pictHandle, (const char *)CFDataGetBytePtr(data), CFDataGetLength(data) ); + } + CFRelease( data ); + + CGImageRelease(cgImageRef); + } +} + +void wxBitmapDataObject::Init() +{ + m_pictHandle = NULL; + m_pictCreated = false; +} + +void wxBitmapDataObject::Clear() +{ + if (m_pictHandle != NULL) + { + DisposeHandle( (Handle) m_pictHandle ); + m_pictHandle = NULL; + } + m_pictCreated = false; +} + +bool wxBitmapDataObject::GetDataHere( void *pBuf ) const +{ + if (m_pictHandle == NULL) + { + wxFAIL_MSG( wxT("attempt to copy empty bitmap failed") ); + return false; + } + + if (pBuf == NULL) + return false; + + memcpy( pBuf, *(Handle)m_pictHandle, GetHandleSize( (Handle)m_pictHandle ) ); + + return true; +} + +size_t wxBitmapDataObject::GetDataSize() const +{ + if (m_pictHandle != NULL) + return GetHandleSize( (Handle)m_pictHandle ); + else + return 0; +} + +Handle MacCreateDataReferenceHandle(Handle theDataHandle) +{ + Handle dataRef = NULL; + OSErr err = noErr; + + // Create a data reference handle for our data. + err = PtrToHand( &theDataHandle, &dataRef, sizeof(Handle)); + + return dataRef; +} + +bool wxBitmapDataObject::SetData( size_t nSize, const void *pBuf ) +{ + Clear(); + + if ((pBuf == NULL) || (nSize == 0)) + return false; + + Handle picHandle = NewHandle( nSize ); + memcpy( *picHandle, pBuf, nSize ); + m_pictHandle = picHandle; + CGImageRef cgImageRef = 0; + + CFDataRef data = CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, (const UInt8*) pBuf, nSize, kCFAllocatorNull); + CGImageSourceRef source = CGImageSourceCreateWithData( data, NULL ); + if ( source ) + { + cgImageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL); + } + CFRelease( source ); + CFRelease( data ); + + if ( cgImageRef ) + { + m_bitmap.Create( CGImageGetWidth(cgImageRef) , CGImageGetHeight(cgImageRef) ); + CGRect r = CGRectMake( 0 , 0 , CGImageGetWidth(cgImageRef) , CGImageGetHeight(cgImageRef) ); + // since our context is upside down we dont use CGContextDrawImage + wxMacDrawCGImage( (CGContextRef) m_bitmap.GetHBITMAP() , &r, cgImageRef ) ; + CGImageRelease(cgImageRef); + cgImageRef = NULL; + } + + return m_bitmap.Ok(); +} + +#endif diff --git a/src/osx/carbon/dataview.cpp b/src/osx/carbon/dataview.cpp new file mode 100644 index 0000000000..5dd666c1ca --- /dev/null +++ b/src/osx/carbon/dataview.cpp @@ -0,0 +1,1575 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/datavgen.cpp +// Purpose: wxDataViewCtrl native mac implementation +// Author: +// Id: $Id$ +// Copyright: (c) 2007 +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_DATAVIEWCTRL + +#include "wx/dataview.h" + +#if !defined(wxUSE_GENERICDATAVIEWCTRL) || (wxUSE_GENERICDATAVIEWCTRL == 0) + +#include + +#include "wx/mac/carbon/databrow.h" + +#ifndef WX_PRECOMP + #include "wx/timer.h" + #include "wx/settings.h" + #include "wx/dcclient.h" + #include "wx/icon.h" +#endif + +#include "wx/renderer.h" + +//----------------------------------------------------------------------------- +// local constants +//----------------------------------------------------------------------------- + +// a list of all catchable events: +static EventTypeSpec const eventList[] = +{ + {kEventClassControl, kEventControlDraw}, + {kEventClassControl, kEventControlHit} +}; + +//----------------------------------------------------------------------------- +// local functions +//----------------------------------------------------------------------------- + +static pascal OSStatus wxMacDataViewCtrlEventHandler(EventHandlerCallRef handler, EventRef EventReference, void* Data) +{ + wxDataViewCtrl* DataViewCtrlPtr((wxDataViewCtrl*) Data); // the 'Data' variable always contains a pointer to the data view control that installed the handler + + wxMacCarbonEvent CarbonEvent(EventReference) ; + + + switch (GetEventKind(EventReference)) + { + case kEventControlDraw: + { + OSStatus status; + + DataViewCtrlPtr->MacSetDrawingContext(CarbonEvent.GetParameter(kEventParamCGContextRef,typeCGContextRef)); + status = ::CallNextEventHandler(handler,EventReference); + DataViewCtrlPtr->MacSetDrawingContext(NULL); + return status; + } /* block */ + case kEventControlHit : + if (CarbonEvent.GetParameter(kEventParamControlPart,typeControlPartCode) == kControlButtonPart) // we only care about the header + { + ControlRef controlReference; + DataBrowserPropertyID columnPropertyID; + unsigned long columnIndex; + OSStatus status; + wxDataViewEvent DataViewEvent(wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK,DataViewCtrlPtr->GetId()); + + CarbonEvent.GetParameter(kEventParamDirectObject,&controlReference); + // determine the column that triggered the event (this is the column that is responsible for sorting the data view): + status = ::GetDataBrowserSortProperty(controlReference,&columnPropertyID); + wxCHECK(status == noErr,status); + status = ::GetDataBrowserTableViewColumnPosition(controlReference,columnPropertyID,&columnIndex); + if (status == errDataBrowserPropertyNotFound) // user clicked into part of the header that does not have a property + return ::CallNextEventHandler(handler,EventReference); + wxCHECK(status == noErr,status); + // initialize wxWidget event handler: + DataViewEvent.SetEventObject(DataViewCtrlPtr); + DataViewEvent.SetColumn(columnIndex); + DataViewEvent.SetDataViewColumn(DataViewCtrlPtr->GetColumn(columnIndex)); + // finally sent the equivalent wxWidget event: +#if wxCHECK_VERSION(2,9,0) + DataViewCtrlPtr->HandleWindowEvent(DataViewEvent); +#else + DataViewCtrlPtr->GetEventHandler()->ProcessEvent(DataViewEvent); +#endif + return ::CallNextEventHandler(handler,EventReference); + } /* if */ + else + return eventNotHandledErr; + } /* switch */ + + return eventNotHandledErr; +} /* wxMacDataViewCtrlEventHandler(EventHandlerCallRef, EventRef, void*) */ + +static DataBrowserItemID* CreateDataBrowserItemIDArray(size_t& noOfEntries, wxDataViewItemArray const& items) // returns a newly allocated pointer to valid data browser item IDs +{ + size_t const noOfItems = items.GetCount(); + + DataBrowserItemID* itemIDs(new DataBrowserItemID[noOfItems]); + + + // convert all valid data view items to data browser items + noOfEntries = 0; + for (size_t i=0; i(items[i].GetID()); + ++noOfEntries; + } /* if */ + // done: + return itemIDs; +} /* CreateDataBrowserItemIDArray(size_t&, wxDataViewItemArray const&) */ + +#if wxCHECK_VERSION(2,9,0) +static bool InitializeColumnDescription(DataBrowserListViewColumnDesc& columnDescription, wxDataViewColumn const* columnPtr, DataBrowserPropertyID columnPropertyID, wxCFStringRef const& title) +#else +static bool InitializeColumnDescription(DataBrowserListViewColumnDesc& columnDescription, wxDataViewColumn const* columnPtr, DataBrowserPropertyID columnPropertyID, wxMacCFStringHolder const& title) +#endif +{ + // set properties for the column: + columnDescription.propertyDesc.propertyID = columnPropertyID; + columnDescription.propertyDesc.propertyType = columnPtr->GetRenderer()->GetPropertyType(); + columnDescription.propertyDesc.propertyFlags = kDataBrowserListViewSelectionColumn; // make the column selectable + if (columnPtr->IsReorderable()) + columnDescription.propertyDesc.propertyFlags |= kDataBrowserListViewMovableColumn; + if (columnPtr->IsResizeable()) + { + columnDescription.headerBtnDesc.minimumWidth = 0; + columnDescription.headerBtnDesc.maximumWidth = 30000; // 32767 is the theoretical maximum though but 30000 looks nicer + } /* if */ + else + { + columnDescription.headerBtnDesc.minimumWidth = columnPtr->GetWidth(); + columnDescription.headerBtnDesc.maximumWidth = columnPtr->GetWidth(); + } /* if */ + if (columnPtr->IsSortable()) + columnDescription.propertyDesc.propertyFlags |= kDataBrowserListViewSortableColumn; + if (columnPtr->GetRenderer()->GetMode() == wxDATAVIEW_CELL_EDITABLE) + columnDescription.propertyDesc.propertyFlags |= kDataBrowserPropertyIsEditable; + if ((columnDescription.propertyDesc.propertyType == kDataBrowserCustomType) || + (columnDescription.propertyDesc.propertyType == kDataBrowserDateTimeType) || + (columnDescription.propertyDesc.propertyType == kDataBrowserIconAndTextType) || + (columnDescription.propertyDesc.propertyType == kDataBrowserTextType)) + columnDescription.propertyDesc.propertyFlags |= kDataBrowserListViewTypeSelectColumn; // enables generally the possibility to have user input for the mentioned types +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + columnDescription.propertyDesc.propertyFlags |= kDataBrowserListViewNoGapForIconInHeaderButton; +#endif + // set header's properties: + columnDescription.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc; + columnDescription.headerBtnDesc.titleOffset = 0; + columnDescription.headerBtnDesc.titleString = ::CFStringCreateCopy(kCFAllocatorDefault,title); + columnDescription.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing; // choose one of the orders as "undefined" is not supported anyway (s. ControlDefs.h in the HIToolbox framework) + columnDescription.headerBtnDesc.btnFontStyle.flags = kControlUseFontMask | kControlUseJustMask; + switch (columnPtr->GetAlignment()) + { + case wxALIGN_CENTER: + case wxALIGN_CENTER_HORIZONTAL: + columnDescription.headerBtnDesc.btnFontStyle.just = teCenter; + break; + case wxALIGN_LEFT: + columnDescription.headerBtnDesc.btnFontStyle.just = teFlushLeft; + break; + case wxALIGN_RIGHT: + columnDescription.headerBtnDesc.btnFontStyle.just = teFlushRight; + break; + default: + columnDescription.headerBtnDesc.btnFontStyle.just = teFlushDefault; + } /* switch */ + columnDescription.headerBtnDesc.btnFontStyle.font = kControlFontViewSystemFont; + columnDescription.headerBtnDesc.btnFontStyle.style = normal; + if (columnPtr->GetBitmap().IsOk()) + { + columnDescription.headerBtnDesc.btnContentInfo.contentType = kControlContentIconRef; +#if wxCHECK_VERSION(2,9,0) + columnDescription.headerBtnDesc.btnContentInfo.u.iconRef = columnPtr->GetBitmap().GetIconRef(); +#else + columnDescription.headerBtnDesc.btnContentInfo.u.iconRef = columnPtr->GetBitmap().GetBitmapData()->GetIconRef(); +#endif + } + else + { + // not text only as we otherwise could not add a bitmap later + // columnDescription.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly; + columnDescription.headerBtnDesc.btnContentInfo.contentType = kControlContentIconRef; + columnDescription.headerBtnDesc.btnContentInfo.u.iconRef = NULL; + } + + // done: + return true; +} /* InitializeColumnDescription(DataBrowserListViewColumnDesc&, wxDataViewColumn const*, DataBrowserPropertyID, wxMacCFStringHolder const&) */ + +//----------------------------------------------------------------------------- +// local function pointers +//----------------------------------------------------------------------------- + +DEFINE_ONE_SHOT_HANDLER_GETTER(wxMacDataViewCtrlEventHandler) + +// --------------------------------------------------------- +// wxMacDataViewModelNotifier +// --------------------------------------------------------- +#pragma mark - +class wxMacDataViewModelNotifier : public wxDataViewModelNotifier +{ +public: + wxMacDataViewModelNotifier(wxMacDataViewDataBrowserListViewControl* initDataViewControlPtr) : m_dataViewControlPtr(initDataViewControlPtr) + { + } + + virtual bool ItemAdded(const wxDataViewItem &parent, const wxDataViewItem &item) + { + DataBrowserItemID itemID(reinterpret_cast(item.GetID())); + + wxCHECK_MSG(item.IsOk(),false,_("Added item is invalid.")); + bool noFailureFlag = (!(parent.IsOk()) && (this->m_dataViewControlPtr->AddItem(kDataBrowserNoItem,&itemID) == noErr) || + parent.IsOk() && (this->m_dataViewControlPtr->AddItem(reinterpret_cast(parent.GetID()),&itemID) == noErr)); + + wxDataViewCtrl *dvc = (wxDataViewCtrl*) this->m_dataViewControlPtr->GetPeer(); + if (dvc->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) + { + wxDataViewModel *model = GetOwner(); + + int height = 20; // TODO find out standard height + unsigned int num = dvc->GetColumnCount(); + unsigned int col; + for (col = 0; col < num; col++) + { + wxDataViewColumn *column = dvc->GetColumn( col ); + if (column->IsHidden()) + continue; + + wxDataViewCustomRenderer *renderer = wxDynamicCast( column->GetRenderer(), wxDataViewCustomRenderer ); + if (renderer) + { + wxVariant value; + model->GetValue( value, item, column->GetModelColumn() ); + renderer->SetValue( value ); + height = wxMax( height, renderer->GetSize().y ); + } + + } + + if (height > 20) + this->m_dataViewControlPtr->SetRowHeight( itemID, height ); + } + + return noFailureFlag; + } /* ItemAdded(wxDataViewItem const&, wxDataViewItem const&) */ + + virtual bool ItemsAdded(wxDataViewItem const& parent, wxDataViewItemArray const& items) + { + bool noFailureFlag; + + DataBrowserItemID* itemIDs; + + size_t noOfEntries; + + + // convert all valid data view items to data browser items: + itemIDs = ::CreateDataBrowserItemIDArray(noOfEntries,items); + // insert all valid items into control: + noFailureFlag = ((noOfEntries == 0) || + !(parent.IsOk()) && (this->m_dataViewControlPtr->AddItems(kDataBrowserNoItem,noOfEntries,itemIDs,kDataBrowserItemNoProperty) == noErr) || + parent.IsOk() && (this->m_dataViewControlPtr->AddItems(reinterpret_cast(parent.GetID()),noOfEntries,itemIDs,kDataBrowserItemNoProperty) == noErr)); + // give allocated array space free again: + delete[] itemIDs; + + wxDataViewCtrl *dvc = (wxDataViewCtrl*) this->m_dataViewControlPtr->GetPeer(); + if (dvc->GetWindowStyle() & wxDV_VARIABLE_LINE_HEIGHT) + { + wxDataViewModel *model = GetOwner(); + unsigned int colnum = dvc->GetColumnCount(); + + size_t i; + size_t count = items.GetCount(); + for (i = 0; i < count; i++) + { + wxDataViewItem item = items[i]; + DataBrowserItemID itemID(reinterpret_cast(item.GetID())); + + int height = 20; // TODO find out standard height + unsigned int col; + for (col = 0; col < colnum; col++) + { + wxDataViewColumn *column = dvc->GetColumn( col ); + if (column->IsHidden()) + continue; // skip it! + + if ((col != 0) && model->IsContainer(item) && !model->HasContainerColumns(item)) + continue; // skip it! + + wxDataViewCustomRenderer *renderer = wxDynamicCast( column->GetRenderer(), wxDataViewCustomRenderer ); + if (renderer) + { + wxVariant value; + model->GetValue( value, item, column->GetModelColumn() ); + renderer->SetValue( value ); + height = wxMax( height, renderer->GetSize().y ); + } + } + + if (height > 20) + this->m_dataViewControlPtr->SetRowHeight( itemID, height ); + } + } + + // done: + return noFailureFlag; + } /* ItemsAdded(wxDataViewItem const&, wxDataViewItemArray const&) */ + + virtual bool ItemChanged(wxDataViewItem const& item) + { + DataBrowserItemID itemID(reinterpret_cast(item.GetID())); + + + wxCHECK_MSG(item.IsOk(),false,_("Changed item is invalid.")); + if (this->m_dataViewControlPtr->UpdateItems(&itemID) == noErr) + { + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->m_dataViewControlPtr->GetPeer())); + + // sent the equivalent wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED,dataViewCtrlPtr->GetId()); // variable defintion + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetItem(item); + // sent the equivalent wxWidget event: +#if wxCHECK_VERSION(2,9,0) + dataViewCtrlPtr->HandleWindowEvent(dataViewEvent); +#else + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); +#endif + // done + return true; + } /* if */ + else + return false; + } /* ItemChanged(wxDataViewItem const&) */ + + virtual bool ItemsChanged(wxDataViewItemArray const& items) + { + bool noFailureFlag; + + DataBrowserItemID* itemIDs; + + size_t noOfEntries; + + + // convert all valid data view items to data browser items: + itemIDs = ::CreateDataBrowserItemIDArray(noOfEntries,items); + // change items (ATTENTION: ONLY ITEMS OF THE ROOT ARE CHANGED BECAUSE THE PARENT PARAMETER IS MISSING): + noFailureFlag = (this->m_dataViewControlPtr->UpdateItems(kDataBrowserNoItem,noOfEntries,itemIDs,kDataBrowserItemNoProperty,kDataBrowserItemNoProperty) == noErr); + if (noFailureFlag) + { + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->m_dataViewControlPtr->GetPeer())); + + // send for all changed items a wxWidget event: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED,dataViewCtrlPtr->GetId()); // variable defintion + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + for (size_t i=0; i(itemIDs[i])); +#if wxCHECK_VERSION(2,9,0) + dataViewCtrlPtr->HandleWindowEvent(dataViewEvent); +#else + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); +#endif + } /* for */ + } /* if */ + // release allocated array space: + delete[] itemIDs; + // done: + return noFailureFlag; + } /* ItemsChanged(wxDataViewItem const&) */ + + virtual bool ItemDeleted(wxDataViewItem const& parent, wxDataViewItem const& item) + { + if (item.IsOk()) + { + // variable definition and initialization: + DataBrowserItemID itemID(reinterpret_cast(item.GetID())); + OSStatus errorStatus; + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->m_dataViewControlPtr->GetPeer())); + + // when this method is called and currently an item is being edited this item may have already been deleted in the model (the passed item and the being edited item have + // not to be identical because the being edited item might be below the passed item in the hierarchy); + // to prevent the control trying to ask the model to update an already deleted item the control is informed that currently a deleting process + // has been started and that variables can currently not be updated even when requested by the system: + dataViewCtrlPtr->SetDeleting(true); + errorStatus = this->m_dataViewControlPtr->RemoveItem(reinterpret_cast(parent.GetID()),&itemID); + // enable automatic updating again: + dataViewCtrlPtr->SetDeleting(false); + return (errorStatus == noErr); + } /* if */ + else + return false; + } /* ItemDeleted(wxDataViewItem const&, wxDataViewItem const&) */ + + virtual bool ItemsDeleted(wxDataViewItem const& parent, wxDataViewItemArray const& items) + { + bool noFailureFlag; + + DataBrowserItemID* itemIDs; + + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->m_dataViewControlPtr->GetPeer())); + + size_t noOfEntries; + + + wxCHECK_MSG(dataViewCtrlPtr != NULL,false,_("Data view control is not correctly initialized")); + // convert all valid data view items to data browser items: + itemIDs = ::CreateDataBrowserItemIDArray(noOfEntries,items); + // when this method is called and currently an item is being edited this item may have already been deleted in the model (the passed item and the being edited item have + // not to be identical because the being edited item might be below the passed item in the hierarchy); + // to prevent the control trying to ask the model to update an already deleted item the control is informed that currently a deleting process + // has been started and that variables can currently not be updated even when requested by the system: + dataViewCtrlPtr->SetDeleting(true); + // insert all valid items into control: + noFailureFlag = ((noOfEntries == 0) || + !(parent.IsOk()) && (this->m_dataViewControlPtr->RemoveItems(kDataBrowserNoItem,noOfEntries,itemIDs,kDataBrowserItemNoProperty) == noErr) || + parent.IsOk() && (this->m_dataViewControlPtr->RemoveItems(reinterpret_cast(parent.GetID()),noOfEntries,itemIDs,kDataBrowserItemNoProperty) == noErr)); + // enable automatic updating again: + dataViewCtrlPtr->SetDeleting(false); + // give allocated array space free again: + delete[] itemIDs; + // done: + return noFailureFlag; + } /* ItemsDeleted(wxDataViewItem const&, wxDataViewItemArray const&) */ + + virtual bool ValueChanged(wxDataViewItem const& item, unsigned int col) + { + DataBrowserItemID itemID(reinterpret_cast(item.GetID())); + DataBrowserItemID parentID; + + DataBrowserPropertyID propertyID; + + wxDataViewCtrl* dataViewCtrlPtr(dynamic_cast(this->m_dataViewControlPtr->GetPeer())); + + + wxCHECK_MSG(item.IsOk(), false,_("Passed item is invalid.")); + wxCHECK_MSG(this->GetOwner() != NULL,false,_("Owner not initialized.")); + wxCHECK_MSG(dataViewCtrlPtr != NULL, false,_("Control is wrongly initialized.")); + parentID = reinterpret_cast(this->GetOwner()->GetParent(item).GetID()); + if ((this->m_dataViewControlPtr->GetPropertyID(col,&propertyID) == noErr) && + (this->m_dataViewControlPtr->UpdateItems(parentID,1,&itemID,dataViewCtrlPtr->GetColumn(col)->GetPropertyID(),propertyID) == noErr)) + { + // variable definition and initialization: + wxDataViewEvent dataViewEvent(wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED,dataViewCtrlPtr->GetId()); + + dataViewEvent.SetEventObject(dataViewCtrlPtr); + dataViewEvent.SetColumn(col); + dataViewEvent.SetItem(item); + // send the equivalent wxWidget event: +#if wxCHECK_VERSION(2,9,0) + dataViewCtrlPtr->HandleWindowEvent(dataViewEvent); +#else + dataViewCtrlPtr->GetEventHandler()->ProcessEvent(dataViewEvent); +#endif + // done + return true; + } /* if */ + else + return false; + } /* ValueChanged(wxDataViewItem const&, unsigned int) */ + + virtual bool Cleared() + { + bool noFailureFlag = (this->m_dataViewControlPtr->RemoveItems() == noErr); + wxDataViewItem item; + wxDataViewItemArray array; + GetOwner()->GetChildren( item, array ); + ItemsAdded( item, array ); + return noFailureFlag; + } /* Cleared() */ + + virtual void Resort() + { + this->m_dataViewControlPtr->Resort(); + } + +protected: +private: +// +// variables +// + wxMacDataViewDataBrowserListViewControl* m_dataViewControlPtr; +}; + +// --------------------------------------------------------- +// wxDataViewRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewRenderer::wxDataViewRenderer(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRendererBase(varianttype,mode,align), m_alignment(align), m_mode(mode) +{ +} /* wxDataViewRenderer::wxDataViewRenderer(wxString const&, wxDataViewCellMode) */ + +void wxDataViewRenderer::SetMode(wxDataViewCellMode mode) +{ + wxDataViewColumn* dataViewColumnPtr; + + + this->m_mode = mode; + dataViewColumnPtr = this->GetOwner(); + if (dataViewColumnPtr != NULL) + { + // variable definition and initialization: + wxDataViewCtrl* dataViewCtrlPtr(dataViewColumnPtr->GetOwner()); + + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserPropertyFlags flags; + + verify_noerr(macDataViewListCtrlPtr->GetPropertyFlags(dataViewColumnPtr->GetPropertyID(),&flags)); + if (mode == wxDATAVIEW_CELL_EDITABLE) + flags |= kDataBrowserPropertyIsEditable; + else + flags &= ~kDataBrowserPropertyIsEditable; + verify_noerr(macDataViewListCtrlPtr->SetPropertyFlags(dataViewColumnPtr->GetPropertyID(),flags)); + } /* if */ + } /* if */ + } /* if */ +} /* wxDataViewRenderer::SetMode(wxDataViewCellMode) */ + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer,wxDataViewRendererBase) + +// --------------------------------------------------------- +// wxDataViewCustomRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewCustomRenderer::wxDataViewCustomRenderer(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRenderer(varianttype,mode,align), m_editorCtrlPtr(NULL), m_DCPtr(NULL) +{ +} /* wxDataViewCustomRenderer::wxDataViewCustomRenderer(wxString const&, wxDataViewCellMode) */ + +wxDataViewCustomRenderer::~wxDataViewCustomRenderer() +{ + if (this->m_DCPtr != NULL) + delete this->m_DCPtr; +} /* wxDataViewCustomRenderer::~wxDataViewCustomRenderer() */ + +void wxDataViewCustomRenderer::RenderText( const wxString &text, int xoffset, wxRect cell, wxDC *dc, int state ) +{ + wxDataViewCtrl *view = GetOwner()->GetOwner(); +// wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ? wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) : view->GetForegroundColour(); + wxColour col = (state & wxDATAVIEW_CELL_SELECTED) ? *wxWHITE : view->GetForegroundColour(); + dc->SetTextForeground(col); + dc->DrawText( text, cell.x + xoffset, cell.y + ((cell.height - dc->GetCharHeight()) / 2)); +} + +wxDC* wxDataViewCustomRenderer::GetDC() +{ + if (this->m_DCPtr == NULL) + { + if ((GetOwner() == NULL) || (GetOwner()->GetOwner() == NULL)) + return NULL; + this->m_DCPtr = new wxWindowDC(this->GetOwner()->GetOwner()); + } /* if */ + return this->m_DCPtr; +} /* wxDataViewCustomRenderer::GetDC() */ + +bool wxDataViewCustomRenderer::Render() +{ + return true; +} /* wxDataViewCustomRenderer::Render() */ + +void wxDataViewCustomRenderer::SetDC(wxDC* newDCPtr) +{ + delete this->m_DCPtr; + this->m_DCPtr = newDCPtr; +} /* wxDataViewCustomRenderer::SetDC(wxDC*) */ + +WXDataBrowserPropertyType wxDataViewCustomRenderer::GetPropertyType() const +{ + return kDataBrowserCustomType; +} /* wxDataViewCustomRenderer::GetPropertyType() const */ + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer) + +// --------------------------------------------------------- +// wxDataViewTextRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewTextRenderer::wxDataViewTextRenderer(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRenderer(varianttype,mode,align) +{ +} /* wxDataViewTextRenderer::wxDataViewTextRenderer(wxString const&, wxDataViewCellMode, int) */ + +bool wxDataViewTextRenderer::Render() +{ + wxCHECK_MSG(this->GetValue().GetType() == this->GetVariantType(),false,wxString(_("Text renderer cannot render value; value type: ")) << this->GetValue().GetType()); + + // variable definition: +#if wxCHECK_VERSION(2,9,0) + wxCFStringRef cfString(this->GetValue().GetString(),(this->GetView()->GetFont().Ok() ? this->GetView()->GetFont().GetEncoding() : wxLocale::GetSystemEncoding())); +#else + wxMacCFStringHolder cfString(this->GetValue().GetString(),(this->GetView()->GetFont().Ok() ? this->GetView()->GetFont().GetEncoding() : wxLocale::GetSystemEncoding())); +#endif + return (::SetDataBrowserItemDataText(this->GetDataReference(),cfString) == noErr); +} /* wxDataViewTextRenderer::Render() */ + +WXDataBrowserPropertyType wxDataViewTextRenderer::GetPropertyType() const +{ + return kDataBrowserTextType; +} /* wxDataViewTextRenderer::GetPropertyType() const */ + +IMPLEMENT_CLASS(wxDataViewTextRenderer,wxDataViewRenderer) + +// --------------------------------------------------------- +// wxDataViewTextRendererAttr +// --------------------------------------------------------- +#pragma mark - +wxDataViewTextRendererAttr::wxDataViewTextRendererAttr(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewTextRenderer(varianttype,mode,align) +{ +} /* wxDataViewTextRendererAttr::wxDataViewTextRendererAttr(wxString const&, wxDataViewCellMode, int) */ + +IMPLEMENT_CLASS(wxDataViewTextRendererAttr,wxDataViewTextRenderer) + +// --------------------------------------------------------- +// wxDataViewBitmapRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewBitmapRenderer::wxDataViewBitmapRenderer(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRenderer(varianttype,mode,align) +{ +} + +bool wxDataViewBitmapRenderer::Render() + // This method returns 'true' if + // - the passed bitmap is valid and it could be assigned to the native data browser; + // - the passed bitmap is invalid (or is not initialized); this case simulates a non-existing bitmap. + // In all other cases the method returns 'false'. +{ + wxCHECK_MSG(this->GetValue().GetType() == this->GetVariantType(),false,wxString(_("Bitmap renderer cannot render value; value type: ")) << this->GetValue().GetType()); + + // variable definition: + wxBitmap bitmap; + + bitmap << this->GetValue(); + if (bitmap.Ok()) +#if wxCHECK_VERSION(2,9,0) + return (::SetDataBrowserItemDataIcon(this->GetDataReference(),bitmap.GetIconRef()) == noErr); +#else + return (::SetDataBrowserItemDataIcon(this->GetDataReference(),bitmap.GetBitmapData()->GetIconRef()) == noErr); +#endif + else + return true; +} /* wxDataViewBitmapRenderer::Render() */ + +WXDataBrowserPropertyType wxDataViewBitmapRenderer::GetPropertyType() const +{ + return kDataBrowserIconType; +} /* wxDataViewBitmapRenderer::GetPropertyType() const */ + +IMPLEMENT_CLASS(wxDataViewBitmapRenderer,wxDataViewRenderer) + +// --------------------------------------------------------- +// wxDataViewIconTextRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewIconTextRenderer::wxDataViewIconTextRenderer(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRenderer(varianttype,mode) +{ +} + +bool wxDataViewIconTextRenderer::Render() +{ + wxCHECK_MSG(this->GetValue().GetType() == this->GetVariantType(),false,wxString(_("Icon & text renderer cannot render value; value type: ")) << this->GetValue().GetType()); + + // variable definition: + wxDataViewIconText iconText; + + iconText << this->GetValue(); + + // variable definition: +#if wxCHECK_VERSION(2,9,0) + wxCFStringRef cfString(iconText.GetText(),(this->GetView()->GetFont().Ok() ? this->GetView()->GetFont().GetEncoding() : wxLocale::GetSystemEncoding())); +#else + wxMacCFStringHolder cfString(iconText.GetText(),(this->GetView()->GetFont().Ok() ? this->GetView()->GetFont().GetEncoding() : wxLocale::GetSystemEncoding())); +#endif + + if (iconText.GetIcon().IsOk()) + if (::SetDataBrowserItemDataIcon(this->GetDataReference(),MAC_WXHICON(iconText.GetIcon().GetHICON())) != noErr) + return false; + return (::SetDataBrowserItemDataText(this->GetDataReference(),cfString) == noErr); +} /* wxDataViewIconTextRenderer::Render() */ + +WXDataBrowserPropertyType wxDataViewIconTextRenderer::GetPropertyType() const +{ + return kDataBrowserIconAndTextType; +} /* wxDataViewIconTextRenderer::GetPropertyType() const */ + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewIconTextRenderer,wxDataViewRenderer) + + +// --------------------------------------------------------- +// wxDataViewToggleRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewToggleRenderer::wxDataViewToggleRenderer(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRenderer(varianttype,mode) +{ +} + +bool wxDataViewToggleRenderer::Render() +{ + wxCHECK_MSG(this->GetValue().GetType() == this->GetVariantType(),false,wxString(_("Toggle renderer cannot render value; value type: ")) << this->GetValue().GetType()); + return (::SetDataBrowserItemDataButtonValue(this->GetDataReference(),this->GetValue().GetBool()) == noErr); +} /* wxDataViewToggleRenderer::Render() */ + +WXDataBrowserPropertyType wxDataViewToggleRenderer::GetPropertyType() const +{ + return kDataBrowserCheckboxType; +} /* wxDataViewToggleRenderer::GetPropertyType() const */ + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleRenderer,wxDataViewRenderer) + +// --------------------------------------------------------- +// wxDataViewProgressRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewProgressRenderer::wxDataViewProgressRenderer(wxString const& label, wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRenderer(varianttype,mode,align) +{ +} + +bool wxDataViewProgressRenderer::Render() +{ + wxCHECK_MSG(this->GetValue().GetType() == this->GetVariantType(),false,wxString(_("Progress renderer cannot render value type; value type: ")) << this->GetValue().GetType()); + return ((::SetDataBrowserItemDataMinimum(this->GetDataReference(), 0) == noErr) && + (::SetDataBrowserItemDataMaximum(this->GetDataReference(),100) == noErr) && + (::SetDataBrowserItemDataValue (this->GetDataReference(),this->GetValue().GetLong()) == noErr)); +} /* wxDataViewProgressRenderer::Render() */ + +WXDataBrowserPropertyType wxDataViewProgressRenderer::GetPropertyType() const +{ + return kDataBrowserProgressBarType; +} /* wxDataViewProgressRenderer::GetPropertyType() const */ + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressRenderer,wxDataViewRenderer) + +// --------------------------------------------------------- +// wxDataViewDateRenderer +// --------------------------------------------------------- +#pragma mark - +wxDataViewDateRenderer::wxDataViewDateRenderer(wxString const& varianttype, wxDataViewCellMode mode, int align) + :wxDataViewRenderer(varianttype,mode,align) +{ +} + +bool wxDataViewDateRenderer::Render() +{ + wxCHECK_MSG(this->GetValue().GetType() == this->GetVariantType(),false,wxString(_("Date renderer cannot render value; value type: ")) << this->GetValue().GetType()); + return (::SetDataBrowserItemDataDateTime(this->GetDataReference(),this->GetValue().GetDateTime().Subtract(wxDateTime(1,wxDateTime::Jan,1904)).GetSeconds().GetLo()) == noErr); +} /* wxDataViewDateRenderer::Render() */ + +WXDataBrowserPropertyType wxDataViewDateRenderer::GetPropertyType() const +{ + return kDataBrowserDateTimeType; +} /* wxDataViewDateRenderer::GetPropertyType() const */ + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer,wxDataViewRenderer) + +// --------------------------------------------------------- +// wxDataViewColumn +// --------------------------------------------------------- +#pragma mark - +wxDataViewColumn::wxDataViewColumn(wxString const &title, wxDataViewRenderer *cell, unsigned int model_column, int width, wxAlignment align, int flags) + :wxDataViewColumnBase(title,cell,model_column,width,align,flags), m_ascending(true), + m_flags(flags & ~(wxDATAVIEW_COL_HIDDEN)), m_maxWidth(30000), m_minWidth(0), m_width(width >= 0 ? width : wxDVC_DEFAULT_WIDTH), + m_alignment(align), m_title(title) +{ +} /* wxDataViewColumn::wxDataViewColumn(wxString const &title, wxDataViewRenderer*, unsigned int, int, wxAlignment, int) */ + +wxDataViewColumn::wxDataViewColumn(wxBitmap const& bitmap, wxDataViewRenderer *cell, unsigned int model_column, int width, wxAlignment align, int flags) + :wxDataViewColumnBase(bitmap,cell,model_column,width,align,flags), m_ascending(true), + m_flags(flags & ~(wxDATAVIEW_COL_HIDDEN)), m_maxWidth(30000), m_minWidth(0), m_width(width >= 0 ? width : wxDVC_DEFAULT_WIDTH), + m_alignment(align) +{ +} /* wxDataViewColumn::wxDataViewColumn(wxBitmap const&, wxDataViewRenderer*, unsigned int, int, wxAlignment, int) */ + +void wxDataViewColumn::SetAlignment(wxAlignment align) +{ + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); + + + this->m_alignment = align; + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserListViewHeaderDesc headerDescription; + + wxCHECK_RET(macDataViewListCtrlPtr->GetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not get header description.")); + switch (align) + { + case wxALIGN_CENTER: + case wxALIGN_CENTER_HORIZONTAL: + headerDescription.btnFontStyle.just = teCenter; + break; + case wxALIGN_LEFT: + headerDescription.btnFontStyle.just = teFlushLeft; + break; + case wxALIGN_RIGHT: + headerDescription.btnFontStyle.just = teFlushRight; + break; + default: + headerDescription.btnFontStyle.just = teFlushDefault; + } /* switch */ + wxCHECK_RET(macDataViewListCtrlPtr->SetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not set alignment.")); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetAlignment(wxAlignment) */ + +void wxDataViewColumn::SetBitmap(wxBitmap const& bitmap) +{ + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); + + + wxDataViewColumnBase::SetBitmap(bitmap); + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserListViewHeaderDesc headerDescription; + + wxCHECK_RET(macDataViewListCtrlPtr->GetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not get header description.")); + if (this->GetBitmap().Ok()) +#if wxCHECK_VERSION(2,9,0) + headerDescription.btnContentInfo.u.iconRef = this->GetBitmap().GetIconRef(); +#else + headerDescription.btnContentInfo.u.iconRef = this->GetBitmap().GetBitmapData()->GetIconRef(); +#endif + else + headerDescription.btnContentInfo.u.iconRef = NULL; + wxCHECK_RET(macDataViewListCtrlPtr->SetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not set icon.")); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetBitmap(wxBitmap const&) */ + +void wxDataViewColumn::SetFlags(int flags) +{ + this->SetHidden ((flags & wxDATAVIEW_COL_HIDDEN) != 0); + this->SetReorderable((flags & wxDATAVIEW_COL_REORDERABLE) != 0); + this->SetResizeable ((flags & wxDATAVIEW_COL_RESIZABLE) != 0); + this->SetSortable ((flags & wxDATAVIEW_COL_SORTABLE) != 0); +} /* wxDataViewColumn::SetFlags(int) */ + +void wxDataViewColumn::SetMaxWidth(int maxWidth) +{ + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); + + + this->m_maxWidth = maxWidth; + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserListViewHeaderDesc headerDescription; + + wxCHECK_RET(macDataViewListCtrlPtr->GetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not get header description.")); + headerDescription.maximumWidth = static_cast(maxWidth); + wxCHECK_RET(macDataViewListCtrlPtr->SetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not set maximum width.")); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetMaxWidth(int) */ + +void wxDataViewColumn::SetMinWidth(int minWidth) +{ + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); + + + this->m_minWidth = minWidth; + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserListViewHeaderDesc headerDescription; + + wxCHECK_RET(macDataViewListCtrlPtr->GetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not get header description.")); + headerDescription.minimumWidth = static_cast(minWidth); + wxCHECK_RET(macDataViewListCtrlPtr->SetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not set minimum width.")); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetMaxWidth(int) */ + +void wxDataViewColumn::SetReorderable(bool reorderable) +{ + // first set the internal flag of the column: + if (reorderable) + this->m_flags |= wxDATAVIEW_COL_REORDERABLE; + else + this->m_flags &= ~wxDATAVIEW_COL_REORDERABLE; + // if the column is associated with a control change also immediately the flags of the control: + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); // variable definition and initialization + + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserPropertyFlags flags; + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + wxCHECK_RET(macDataViewListCtrlPtr != NULL, _("Valid pointer to native data view control does not exist")); + wxCHECK_RET(macDataViewListCtrlPtr->GetPropertyFlags(this->GetPropertyID(),&flags) == noErr,_("Could not get property flags.")); + if (reorderable) + flags |= kDataBrowserListViewMovableColumn; + else + flags &= ~kDataBrowserListViewMovableColumn; + wxCHECK_RET(macDataViewListCtrlPtr->SetPropertyFlags(this->GetPropertyID(),flags) == noErr,_("Could not set property flags.")); + } /* if */ +} /* wxDataViewColumn::SetReorderable(bool) */ + +void wxDataViewColumn::SetResizeable(bool resizeable) +{ + // first set the internal flag of the column: + if (resizeable) + this->m_flags |= wxDATAVIEW_COL_RESIZABLE; + else + this->m_flags &= ~wxDATAVIEW_COL_RESIZABLE; + // if the column is associated with a control change also immediately the flags of the control: + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); // variable definition and initialization + + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserListViewHeaderDesc headerDescription; + + verify_noerr(macDataViewListCtrlPtr->GetHeaderDesc(this->GetPropertyID(),&headerDescription)); + if (resizeable) { + headerDescription.minimumWidth = 0; + headerDescription.maximumWidth = 30000; + } + else { + headerDescription.minimumWidth = this->m_width; + headerDescription.maximumWidth = this->m_width; + } + verify_noerr(macDataViewListCtrlPtr->SetHeaderDesc(this->GetPropertyID(),&headerDescription)); + macDataViewListCtrlPtr->SetSortProperty(this->GetPropertyID()); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetResizeable(bool) */ + +void wxDataViewColumn::SetSortable(bool sortable) +{ + // first set the internal flag of the column: + if (sortable) + this->m_flags |= wxDATAVIEW_COL_SORTABLE; + else + this->m_flags &= ~wxDATAVIEW_COL_SORTABLE; + // if the column is associated with a control change also immediately the flags of the control: + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); // variable definition and initialization + + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserPropertyFlags flags; + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + wxCHECK_RET(macDataViewListCtrlPtr != NULL, _("Valid pointer to native data view control does not exist")); + wxCHECK_RET(macDataViewListCtrlPtr->GetPropertyFlags(this->GetPropertyID(),&flags) == noErr,_("Could not get property flags.")); + if (sortable) + flags |= kDataBrowserListViewSortableColumn; + else + flags &= ~kDataBrowserListViewSortableColumn; + wxCHECK_RET(macDataViewListCtrlPtr->SetPropertyFlags(this->GetPropertyID(),flags) == noErr,_("Could not set property flags.")); + } /* if */ +} /* wxDataViewColumn::SetSortable(bool) */ + +void wxDataViewColumn::SetSortOrder(bool ascending) +{ + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); + + + this->m_ascending = ascending; + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserListViewHeaderDesc headerDescription; + + verify_noerr(macDataViewListCtrlPtr->GetHeaderDesc(this->GetPropertyID(),&headerDescription)); + if (ascending) + headerDescription.initialOrder = kDataBrowserOrderIncreasing; + else + headerDescription.initialOrder = kDataBrowserOrderDecreasing; + verify_noerr(macDataViewListCtrlPtr->SetHeaderDesc(this->GetPropertyID(),&headerDescription)); + macDataViewListCtrlPtr->SetSortProperty(this->GetPropertyID()); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetSortOrder(bool) */ + +void wxDataViewColumn::SetTitle(wxString const& title) +{ + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); + + + this->m_title = title; + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + { + // variable definition and initialization: + DataBrowserListViewHeaderDesc headerDescription; +#if wxCHECK_VERSION(2,9,0) + wxCFStringRef cfTitle(title,(dataViewCtrlPtr->GetFont().Ok() ? dataViewCtrlPtr->GetFont().GetEncoding() : wxLocale::GetSystemEncoding())); +#else + wxMacCFStringHolder cfTitle(title,(dataViewCtrlPtr->GetFont().Ok() ? dataViewCtrlPtr->GetFont().GetEncoding() : wxLocale::GetSystemEncoding())); +#endif + + wxCHECK_RET(macDataViewListCtrlPtr->GetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not get header description.")); + headerDescription.titleString = cfTitle; + wxCHECK_RET(macDataViewListCtrlPtr->SetHeaderDesc(this->GetPropertyID(),&headerDescription) == noErr,_("Could not set header description.")); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetTitle(wxString const&) */ + +void wxDataViewColumn::SetWidth(int width) +{ + wxDataViewCtrl* dataViewCtrlPtr(this->GetOwner()); + + + if ((width >= this->m_minWidth) && (width <= this->m_maxWidth)) + { + this->m_width = width; + if (dataViewCtrlPtr != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer macDataViewListCtrlPtr(dynamic_cast(dataViewCtrlPtr->GetPeer())); + + if (macDataViewListCtrlPtr != NULL) + wxCHECK_RET(macDataViewListCtrlPtr->SetColumnWidth(this->GetPropertyID(),static_cast(width)) == noErr,_("Could not set column width.")); + } /* if */ + } /* if */ +} /* wxDataViewColumn::SetWidth(int) */ + +IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn,wxDataViewColumnBase) + +//----------------------------------------------------------------------------- +// wxDataViewCtrl +//----------------------------------------------------------------------------- + + +wxDataViewCtrl::~wxDataViewCtrl() +{ + ClearColumns(); +} + +#pragma mark - +void wxDataViewCtrl::Init() +{ + this->m_CustomRendererPtr = NULL; + this->m_Deleting = false; + this->m_macIsUserPane = false; + this->m_cgContext = NULL; +} /* wxDataViewCtrl::Init() */ + +bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator ) +{ + if (!(this->wxControl::Create(parent,id,pos,size,(style | wxSUNKEN_BORDER) & ~(wxHSCROLL | wxVSCROLL),validator))) + return false; + +#ifdef __WXMAC__ + MacSetClipChildren(true) ; +#endif + + this->m_peer = new wxMacDataViewDataBrowserListViewControl(this,pos,size,style); + this->MacPostControlCreate(pos,size); + ::SetAutomaticControlDragTrackingEnabledForWindow(::GetControlOwner(this->m_peer->GetControlRef()),true); + + InstallControlEventHandler(this->m_peer->GetControlRef(),GetwxMacDataViewCtrlEventHandlerUPP(),GetEventTypeCount(eventList),eventList,this,NULL); + + ::SetDataBrowserTableViewHiliteStyle( this->m_peer->GetControlRef(), kDataBrowserTableViewFillHilite ); + + return true; +} /* wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator) */ + +/*static*/ +wxVisualAttributes wxDataViewCtrl::GetClassDefaultAttributes(wxWindowVariant variant) +{ + wxVisualAttributes attr; + + attr.colFg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ); + attr.colBg = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX ); + attr.font.MacCreateFromThemeFont(kThemeViewsFont); + + return attr; +} + +bool wxDataViewCtrl::AssociateModel(wxDataViewModel* model) +{ + if (!wxDataViewCtrlBase::AssociateModel(model)) + return false; + + model->AddNotifier(new wxMacDataViewModelNotifier(dynamic_cast(this->m_peer))); + + return true; +} + +bool wxDataViewCtrl::AppendColumn(wxDataViewColumn* columnPtr) +{ + return InsertColumn( kDataBrowserListViewAppendColumn, columnPtr ); +} + +bool wxDataViewCtrl::PrependColumn(wxDataViewColumn* columnPtr) +{ + return InsertColumn( 0, columnPtr ); +} + +bool wxDataViewCtrl::InsertColumn(unsigned int pos, wxDataViewColumn* columnPtr) +{ + DataBrowserListViewColumnDesc columnDescription; + + DataBrowserPropertyID NewPropertyID; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + +#if wxCHECK_VERSION(2,9,0) + wxCFStringRef title(columnPtr->GetTitle(),this->m_font.Ok() ? this->GetFont().GetEncoding() : wxLocale::GetSystemEncoding()); +#else + wxMacCFStringHolder title(columnPtr->GetTitle(),this->m_font.Ok() ? this->GetFont().GetEncoding() : wxLocale::GetSystemEncoding()); +#endif + + + // first, some error checking: + wxCHECK_MSG(MacDataViewListCtrlPtr != NULL, false,_("m_peer is not or incorrectly initialized")); + wxCHECK_MSG(columnPtr != NULL, false,_("Column pointer must not be NULL.")); + wxCHECK_MSG(columnPtr->GetRenderer() != NULL, false,_("Column does not have a renderer.")); + wxCHECK_MSG(this->GetModel() != NULL, false,_("No model associated with control.")); + wxCHECK_MSG((columnPtr->GetModelColumn() >= 0) && + (columnPtr->GetModelColumn() < this->GetModel()->GetColumnCount()),false,_("Column's model column has no equivalent in the associated model.")); + + // try to get new ID for the column: + wxCHECK_MSG(MacDataViewListCtrlPtr->GetFreePropertyID(&NewPropertyID) == noErr,false,_("Cannot create new column's ID. Probably max. number of columns reached.")); + // full column variable initialization: + columnPtr->SetPropertyID(NewPropertyID); + // add column to wxWidget's internal structure: + wxCHECK_MSG(this->wxDataViewCtrlBase::AppendColumn(columnPtr) && + this->m_ColumnPointers.insert(ColumnPointerHashMapType::value_type(NewPropertyID,columnPtr)).second,false,_("Could not add column to internal structures.")); + // create a column description and add column to the native control: + wxCHECK_MSG(::InitializeColumnDescription(columnDescription,columnPtr,NewPropertyID,title), false,_("Column description could not be initialized.")); + wxCHECK_MSG(MacDataViewListCtrlPtr->AddColumn(&columnDescription,pos) == noErr,false,_("Column could not be added.")); + + // final adjustments for the layout: + wxCHECK_MSG(MacDataViewListCtrlPtr->SetColumnWidth(NewPropertyID,columnPtr->GetWidth()) == noErr,false,_("Column width could not be set.")); + + // make sure that the data is up-to-date... + // if the newly appended column is the first column add the initial data to the control and mark the column as an expander column, + // otherwise ask the control to 'update' the data in the newly appended column: + if (this->GetColumnCount() == 1) + { + this->SetExpanderColumn(columnPtr); + this->AddChildrenLevel(wxDataViewItem()); + } /* if */ + else + MacDataViewListCtrlPtr->UpdateItems(kDataBrowserNoItem,0,NULL,kDataBrowserItemNoProperty,NewPropertyID); + // done: + return true; +} + +bool wxDataViewCtrl::ClearColumns() +{ + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + while (this->m_ColumnPointers.begin() != this->m_ColumnPointers.end()) + { + wxCHECK_MSG(MacDataViewListCtrlPtr->RemoveColumnByProperty(this->m_ColumnPointers.begin()->first) == noErr,false,_("Could not remove column.")); + delete this->m_ColumnPointers.begin()->second; + this->m_ColumnPointers.erase(this->m_ColumnPointers.begin()); + } /* while */ + return true; +} + +bool wxDataViewCtrl::DeleteColumn(wxDataViewColumn* columnPtr) +{ + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + if ((MacDataViewListCtrlPtr->RemoveColumnByProperty(columnPtr->GetPropertyID()) == noErr) && (this->m_ColumnPointers.erase(columnPtr->GetPropertyID()) > 0)) + { + delete columnPtr; + return true; + } /* if */ + else + return false; +} /* wxDataViewCtrl::DeleteColumn(wxDataViewColumn*) */ + +wxDataViewColumn* wxDataViewCtrl::GetColumn(unsigned int pos) const +{ + DataBrowserPropertyID propertyID; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + if (MacDataViewListCtrlPtr->GetPropertyID(pos,&propertyID) == noErr) + { + // variable definition: + ColumnPointerHashMapType::const_iterator Result(this->m_ColumnPointers.find(propertyID)); + + if (Result != this->m_ColumnPointers.end()) + return Result->second; + else + return NULL; + } /* if */ + else + return NULL; +} /* wxDataViewCtrl::GetColumn(unsigned int pos) const */ + +unsigned int wxDataViewCtrl::GetColumnCount() const +{ + return this->m_ColumnPointers.size(); +} /* wxDataViewCtrl::GetColumnCount() const */ + +int wxDataViewCtrl::GetColumnPosition(wxDataViewColumn const* columnPtr) const +{ + if (columnPtr != NULL) + { + // variable definition and initialization: + DataBrowserTableViewColumnIndex Position; + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + wxCHECK_MSG(MacDataViewListCtrlPtr->GetColumnIndex(columnPtr->GetPropertyID(),&Position) == noErr,-1,_("Could not determine column's position")); + return static_cast(Position); + } /* if */ + else + return wxNOT_FOUND; +} /* wxDataViewCtrl::GetColumnPosition(wxDataViewColumn const*) const */ + +void wxDataViewCtrl::Collapse(wxDataViewItem const& item) +{ + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + MacDataViewListCtrlPtr->CloseContainer(reinterpret_cast(item.GetID())); +} /* wxDataViewCtrl::Collapse(wxDataViewItem const&) */ + +void wxDataViewCtrl::EnsureVisible(wxDataViewItem const& item, wxDataViewColumn const* columnPtr) +{ + if (item.IsOk()) + { + // variable definition and initialization: + DataBrowserPropertyID propertyID; + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + if (columnPtr != NULL) + propertyID = columnPtr->GetPropertyID(); + else + propertyID = kDataBrowserNoItem; + MacDataViewListCtrlPtr->RevealItem(reinterpret_cast(item.GetID()),propertyID,kDataBrowserRevealOnly); + } /* if */ +} /* wxDataViewCtrl::EnsureVisible(wxDataViewItem const&, wxDataViewColumn const*) */ + +void wxDataViewCtrl::Expand(wxDataViewItem const& item) +{ + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + MacDataViewListCtrlPtr->OpenContainer(reinterpret_cast(item.GetID())); +} /* wxDataViewCtrl::Expand(wxDataViewItem const&) */ + +wxDataViewColumn* wxDataViewCtrl::GetSortingColumn() const +{ + DataBrowserPropertyID propertyID; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + if (MacDataViewListCtrlPtr->GetSortProperty(&propertyID) == noErr) + return this->GetColumnPtr(propertyID); + else + return NULL; +} /* wxDataViewCtrl::GetSortingColumn() const */ + +unsigned int wxDataViewCtrl::GetCount() const +{ + ItemCount noOfItems; + + + wxCHECK_MSG(dynamic_cast(this->m_peer)->GetItemCount(&noOfItems) == noErr,0,_("Could not determine number of items")); + return noOfItems; +} /* wxDataViewCtrl::GetCount() const */ + +wxRect wxDataViewCtrl::GetItemRect(wxDataViewItem const& item, wxDataViewColumn const* columnPtr) const +{ + if (item.IsOk() && (columnPtr != NULL)) + { + // variable definition: + Rect MacRectangle; + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + if (MacDataViewListCtrlPtr->GetPartBounds(reinterpret_cast(item.GetID()),columnPtr->GetPropertyID(),kDataBrowserPropertyContentPart,&MacRectangle) == noErr) + { + // variable definition: + wxRect rectangle; + + ::wxMacNativeToRect(&MacRectangle,&rectangle); + return rectangle; + } /* if */ + else + return wxRect(); + } /* if */ + else + return wxRect(); +} /* wxDataViewCtrl::GetItemRect(wxDataViewItem const&, unsigned int) const */ + +wxDataViewItem wxDataViewCtrl::GetSelection() const +{ + wxArrayDataBrowserItemID itemIDs; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + if (MacDataViewListCtrlPtr->GetSelectedItemIDs(itemIDs) > 0) + return wxDataViewItem(reinterpret_cast(itemIDs[0])); + else + return wxDataViewItem(); +} /* wxDataViewCtrl::GetSelection() const */ + +int wxDataViewCtrl::GetSelections(wxDataViewItemArray& sel) const +{ + size_t NoOfSelectedItems; + + wxArrayDataBrowserItemID itemIDs; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + NoOfSelectedItems = MacDataViewListCtrlPtr->GetSelectedItemIDs(itemIDs); + sel.Empty(); + sel.SetCount(NoOfSelectedItems); + for (size_t i=0; i(itemIDs[i])); + return static_cast(NoOfSelectedItems); +} /* wxDataViewCtrl::GetSelections(wxDataViewItemArray&) const */ + +void wxDataViewCtrl::HitTest(wxPoint const& point, wxDataViewItem& item, wxDataViewColumn*& columnPtr) const +{ + item = wxDataViewItem(); + columnPtr = NULL; +} /* wxDataViewCtrl::HitTest(wxPoint const&, wxDataViewItem&, wxDataViewColumn*&) const */ + +bool wxDataViewCtrl::IsSelected(wxDataViewItem const& item) const +{ + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + return MacDataViewListCtrlPtr->IsItemSelected(reinterpret_cast(item.GetID())); +} /* wxDataViewCtrl::IsSelected(wxDataViewItem const&) const */ + +void wxDataViewCtrl::SelectAll() +{ + DataBrowserItemID* itemIDPtr; + + Handle handle(::NewHandle(0)); + + size_t NoOfItems; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + wxCHECK_RET(MacDataViewListCtrlPtr->GetItems(kDataBrowserNoItem,true,kDataBrowserItemAnyState,handle) == noErr,_("Could not get items.")); + NoOfItems = static_cast(::GetHandleSize(handle)/sizeof(DataBrowserItemID)); + HLock(handle); + itemIDPtr = (DataBrowserItemID*) (*handle); + MacDataViewListCtrlPtr->SetSelectedItems(NoOfItems,itemIDPtr,kDataBrowserItemsAssign); + HUnlock(handle); + DisposeHandle(handle); +} /* wxDataViewCtrl::SelectAll() */ + +void wxDataViewCtrl::Select(wxDataViewItem const& item) +{ + if (item.IsOk()) + { + // variable definition and initialization: + DataBrowserItemID itemID(reinterpret_cast(item.GetID())); + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + MacDataViewListCtrlPtr->SetSelectedItems(1,&itemID,kDataBrowserItemsAdd); + } /* if */ +} /* wxDataViewCtrl::Select(wxDataViewItem const&) */ + +void wxDataViewCtrl::SetSelections(wxDataViewItemArray const& sel) +{ + size_t const NoOfSelections = sel.GetCount(); + + DataBrowserItemID* itemIDs; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + itemIDs = new DataBrowserItemID[NoOfSelections]; + for (size_t i=0; i(sel[i].GetID()); + MacDataViewListCtrlPtr->SetSelectedItems(NoOfSelections,itemIDs,kDataBrowserItemsAssign); + delete[] itemIDs; +} /* wxDataViewCtrl::SetSelections(wxDataViewItemArray const&) */ + +void wxDataViewCtrl::Unselect(wxDataViewItem const& item) +{ + if (item.IsOk()) + { + // variable definition and initialization: + DataBrowserItemID itemID(reinterpret_cast(item.GetID())); + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + MacDataViewListCtrlPtr->SetSelectedItems(1,&itemID,kDataBrowserItemsRemove); + } /* if */ +} /* wxDataViewCtrl::Unselect(wxDataViewItem const&) */ + +void wxDataViewCtrl::UnselectAll() +{ + DataBrowserItemID* itemIDPtr; + + Handle handle(::NewHandle(0)); + + size_t NoOfItems; + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + + wxCHECK_RET(MacDataViewListCtrlPtr->GetItems(kDataBrowserNoItem,true,kDataBrowserItemAnyState,handle) == noErr,_("Could not get items.")); + NoOfItems = static_cast(::GetHandleSize(handle)/sizeof(DataBrowserItemID)); + HLock(handle); + itemIDPtr = (DataBrowserItemID*) (*handle); + MacDataViewListCtrlPtr->SetSelectedItems(NoOfItems,itemIDPtr,kDataBrowserItemsRemove); + HUnlock(handle); + DisposeHandle(handle); +} /* wxDataViewCtrl::UnselectAll() */ + +// data handling: +void wxDataViewCtrl::AddChildrenLevel(wxDataViewItem const& parentItem) +{ + int NoOfChildren; + + wxDataViewItemArray items; + + + wxCHECK_RET(this->GetModel() != NULL,_("Model pointer not initialized.")); + NoOfChildren = this->GetModel()->GetChildren(parentItem,items); +#if 0 + for (int i=0; iGetModel()->ItemAdded(parentItem,items[i]); +#else + (void) this->GetModel()->ItemsAdded(parentItem,items); +#endif +} + +void wxDataViewCtrl::FinishCustomItemEditing() +{ + if (this->GetCustomRendererItem().IsOk()) + { + this->GetCustomRendererPtr()->FinishEditing(); + this->SetCustomRendererItem(wxDataViewItem()); + this->SetCustomRendererPtr (NULL); + } +} + +wxDataViewColumn* wxDataViewCtrl::GetColumnPtr(WXDataBrowserPropertyID propertyID) const +{ + // variable definition: + ColumnPointerHashMapType::const_iterator Result(this->m_ColumnPointers.find(propertyID)); + + if (Result != this->m_ColumnPointers.end()) + return Result->second; + else + return NULL; +} + +// inherited methods from wxDataViewCtrlBase +void wxDataViewCtrl::DoSetExpanderColumn() +{ + if (this->GetExpanderColumn() != NULL) + { + // variable definition and initialization: + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + (void) MacDataViewListCtrlPtr->SetDisclosureColumn(this->GetExpanderColumn()->GetPropertyID(),false); // second parameter explicitely passed to ensure that arrow is centered + } +} + +void wxDataViewCtrl::DoSetIndent() +{ + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + + (void) MacDataViewListCtrlPtr->SetIndent(static_cast(this->GetIndent())); +} + +// event handling: +void wxDataViewCtrl::OnSize(wxSizeEvent& event) +{ + unsigned int const NoOfColumns = this->GetColumnCount(); + + + for (unsigned int i=0; iGetColumn(i)); + + if (dataViewColumnPtr != NULL) + { + // variable definition and initialization: + wxDataViewCustomRenderer* dataViewCustomRendererPtr(dynamic_cast(dataViewColumnPtr->GetRenderer())); + + if (dataViewCustomRendererPtr != NULL) + dataViewCustomRendererPtr->SetDC(NULL); // reset DC because DC has changed + } /* if */ + } /* for */ + + wxMacDataViewDataBrowserListViewControlPointer MacDataViewListCtrlPtr(dynamic_cast(this->m_peer)); + ControlRef ref = MacDataViewListCtrlPtr->GetControlRef(); + if (NoOfColumns == 1) + { + ::SetDataBrowserHasScrollBars( ref, false, true ); + ::AutoSizeDataBrowserListViewColumns( ref ); + } + if (NoOfColumns > 1) + { + ::SetDataBrowserHasScrollBars( ref, true, true ); + } + + event.Skip(); +} /* wxDataViewCtrl::OnSize(wxSizeEvent&) */ + +IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl,wxDataViewCtrlBase) + +BEGIN_EVENT_TABLE(wxDataViewCtrl,wxDataViewCtrlBase) + EVT_SIZE(wxDataViewCtrl::OnSize) +END_EVENT_TABLE() + +#endif + // !wxUSE_GENERICDATAVIEWCTRL + +#endif + // wxUSE_DATAVIEWCTRL + diff --git a/src/osx/carbon/dcclient.cpp b/src/osx/carbon/dcclient.cpp new file mode 100644 index 0000000000..a7f4cb2536 --- /dev/null +++ b/src/osx/carbon/dcclient.cpp @@ -0,0 +1,194 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dcclient.cpp +// Purpose: wxClientDCImpl class +// Author: Stefan Csomor +// Modified by: +// Created: 01/02/97 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/dcclient.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/window.h" + #include "wx/dcmemory.h" + #include "wx/settings.h" + #include "wx/toplevel.h" + #include "wx/math.h" + #include "wx/region.h" +#endif + +#include "wx/graphics.h" +#include "wx/rawbmp.h" +#include "wx/mac/private.h" + +//----------------------------------------------------------------------------- +// wxWindowDCImpl +//----------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl, wxGCDCImpl) + +wxWindowDCImpl::wxWindowDCImpl( wxDC *owner ) + : wxGCDCImpl( owner ) +{ + m_release = false; +} + +wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) + : wxGCDCImpl( owner ) +{ + m_window = window; + + WindowRef rootwindow = (WindowRef) window->MacGetTopLevelWindowRef() ; + if (!rootwindow) + return; + + m_ok = true ; + + m_window->GetSize( &m_width , &m_height); + if ( !m_window->IsShownOnScreen() ) + m_width = m_height = 0; + CGContextRef cg = (CGContextRef) window->MacGetCGContextRef(); + m_release = false; + if ( cg == NULL ) + { + SetGraphicsContext( wxGraphicsContext::Create( window ) ) ; + } + else + { + CGContextSaveGState( cg ); + m_release = true ; + // make sure the context is having its origin at the wx-window coordinates of the + // view (read at the top of window.cpp about the differences) + if ( window->MacGetLeftBorderSize() != 0 || window->MacGetTopBorderSize() != 0 ) + CGContextTranslateCTM( cg , -window->MacGetLeftBorderSize() , -window->MacGetTopBorderSize() ); + + SetGraphicsContext( wxGraphicsContext::CreateFromNative( cg ) ); + } + DoSetClippingRegion( 0 , 0 , m_width , m_height ) ; + + SetBackground(wxBrush(window->GetBackgroundColour(),wxSOLID)); + + SetFont( window->GetFont() ) ; +} + +wxWindowDCImpl::~wxWindowDCImpl() +{ + if ( m_release ) + { + // this must not necessarily be the current context, we must restore the state of the + // cg we started with above (before the CGContextTranslateCTM call) + CGContextRef cg = (CGContextRef) m_window->MacGetCGContextRef(); + CGContextRestoreGState(cg); + } +} + +void wxWindowDCImpl::DoGetSize( int* width, int* height ) const +{ + if ( width ) + *width = m_width; + if ( height ) + *height = m_height; +} + +wxBitmap wxWindowDCImpl::DoGetAsBitmap(const wxRect *subrect) const +{ + // wxScreenDC is derived from wxWindowDC, so a screen dc will + // call this method when a Blit is performed with it as a source. + if (!m_window) + return wxNullBitmap; + +#ifdef __LP64__ + return wxNullBitmap; +#else + ControlRef handle = (ControlRef) m_window->GetHandle(); + if ( !handle ) + return wxNullBitmap; + + HIRect rect; + CGImageRef image; + CGContextRef context; + void* data; + + size_t bytesPerRow; + + HIViewCreateOffscreenImage( handle, 0, &rect, &image); + + + int width = subrect != NULL ? subrect->width : (int)rect.size.width; + int height = subrect != NULL ? subrect->height : (int)rect.size.height ; + + wxBitmap bmp = wxBitmap(width, height, 32); + + context = (CGContextRef)bmp.GetHBITMAP(); + + CGContextSaveGState(context); + + CGContextTranslateCTM( context, 0, height ); + CGContextScaleCTM( context, 1, -1 ); + + if ( subrect ) + rect = CGRectOffset( rect, -subrect->x, -subrect->y ) ; + CGContextDrawImage( context, rect, image ); + + CGContextRestoreGState(context); + + return bmp; +#endif +} + +/* + * wxClientDCImpl + */ + +IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl, wxWindowDCImpl) + +wxClientDCImpl::wxClientDCImpl( wxDC *owner ) + : wxWindowDCImpl( owner ) +{ +} + +wxClientDCImpl::wxClientDCImpl( wxDC *owner, wxWindow *window ) : + wxWindowDCImpl( owner, window ) +{ + wxCHECK_RET( window, _T("invalid window in wxClientDCImpl") ); + wxPoint origin = window->GetClientAreaOrigin() ; + m_window->GetClientSize( &m_width , &m_height); + if ( !m_window->IsShownOnScreen() ) + m_width = m_height = 0; + SetDeviceOrigin( origin.x, origin.y ); + DoSetClippingRegion( 0 , 0 , m_width , m_height ) ; +} + +wxClientDCImpl::~wxClientDCImpl() +{ +} + +/* + * wxPaintDCImpl + */ + +IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxWindowDCImpl) + +wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) + : wxWindowDCImpl( owner ) +{ +} + +wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) : + wxWindowDCImpl( owner, window ) +{ + wxPoint origin = window->GetClientAreaOrigin() ; + m_window->GetClientSize( &m_width , &m_height); + SetDeviceOrigin( origin.x, origin.y ); + DoSetClippingRegion( 0 , 0 , m_width , m_height ) ; +} + +wxPaintDCImpl::~wxPaintDCImpl() +{ +} diff --git a/src/osx/carbon/dcmemory.cpp b/src/osx/carbon/dcmemory.cpp new file mode 100644 index 0000000000..4567352d94 --- /dev/null +++ b/src/osx/carbon/dcmemory.cpp @@ -0,0 +1,116 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dcmemory.cpp +// Purpose: wxMemoryDC class +// Author: Stefan Csomor +// Modified by: +// Created: 01/02/97 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/dcmemory.h" +#include "wx/graphics.h" +#include "wx/mac/dcmemory.h" + +#include "wx/mac/private.h" + +//----------------------------------------------------------------------------- +// wxMemoryDCImpl +//----------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxMemoryDCImpl,wxPaintDCImpl) + + +wxMemoryDCImpl::wxMemoryDCImpl( wxMemoryDC *owner ) + : wxPaintDCImpl( owner ) +{ + Init(); +} + +wxMemoryDCImpl::wxMemoryDCImpl( wxMemoryDC *owner, wxBitmap& bitmap ) + : wxPaintDCImpl( owner ) +{ + Init(); + DoSelect(bitmap); +} + +wxMemoryDCImpl::wxMemoryDCImpl( wxMemoryDC *owner, wxDC * WXUNUSED(dc) ) + : wxPaintDCImpl( owner ) +{ + Init(); +} + +void wxMemoryDCImpl::Init() +{ + m_ok = true; + SetBackground(*wxWHITE_BRUSH); + SetBrush(*wxWHITE_BRUSH); + SetPen(*wxBLACK_PEN); + SetFont(*wxNORMAL_FONT); + m_ok = false; +} + +wxMemoryDCImpl::~wxMemoryDCImpl() +{ + if ( m_selected.Ok() ) + { + m_selected.EndRawAccess() ; + delete m_graphicContext ; + m_graphicContext = NULL ; + } +} + +void wxMemoryDCImpl::DoSelect( const wxBitmap& bitmap ) +{ + if ( m_selected.Ok() ) + { + m_selected.EndRawAccess() ; + delete m_graphicContext ; + m_graphicContext = NULL ; + } + + m_selected = bitmap; + if (m_selected.Ok()) + { + if ( m_selected.GetDepth() != 1 ) + m_selected.UseAlpha() ; + m_selected.BeginRawAccess() ; + m_width = bitmap.GetWidth(); + m_height = bitmap.GetHeight(); + CGColorSpaceRef genericColorSpace = wxMacGetGenericRGBColorSpace(); + CGContextRef bmCtx = (CGContextRef) m_selected.GetHBITMAP(); + + if ( bmCtx ) + { + CGContextSetFillColorSpace( bmCtx, genericColorSpace ); + CGContextSetStrokeColorSpace( bmCtx, genericColorSpace ); + SetGraphicsContext( wxGraphicsContext::CreateFromNative( bmCtx ) ); + } + m_ok = (m_graphicContext != NULL) ; + } + else + { + m_ok = false; + } +} + +void wxMemoryDCImpl::DoGetSize( int *width, int *height ) const +{ + if (m_selected.Ok()) + { + if (width) + (*width) = m_selected.GetWidth(); + if (height) + (*height) = m_selected.GetHeight(); + } + else + { + if (width) + (*width) = 0; + if (height) + (*height) = 0; + } +} diff --git a/src/osx/carbon/dcprint.cpp b/src/osx/carbon/dcprint.cpp new file mode 100644 index 0000000000..ccc58d99ff --- /dev/null +++ b/src/osx/carbon/dcprint.cpp @@ -0,0 +1,393 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dcprint.cpp +// Purpose: wxPrinterDC class +// Author: Julian Smart +// Modified by: +// Created: 01/02/97 +// RCS-ID: $Id$ +// Copyright: (c) Julian Smart +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_PRINTING_ARCHITECTURE + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/dcprint.h" + +#ifndef WX_PRECOMP + #include "wx/msgdlg.h" + #include "wx/math.h" +#endif + +#include "wx/mac/uma.h" +#include "wx/mac/private/print.h" +#include "wx/mac/carbon/dcprint.h" +#include "wx/graphics.h" + +IMPLEMENT_ABSTRACT_CLASS(wxPrinterDCImpl, wxGCDCImpl) + +class wxNativePrinterDC +{ +public : + wxNativePrinterDC() {} + virtual ~wxNativePrinterDC() {} + virtual bool StartDoc( wxPrinterDC* dc , const wxString& message ) = 0; + virtual void EndDoc( wxPrinterDC* dc ) = 0; + virtual void StartPage( wxPrinterDC* dc ) = 0; + virtual void EndPage( wxPrinterDC* dc ) = 0; + virtual void GetSize( int *w , int *h) const = 0 ; + virtual wxSize GetPPI() const = 0 ; + + // returns 0 in case of no Error, otherwise platform specific error codes + virtual wxUint32 GetStatus() const = 0 ; + bool Ok() { return GetStatus() == 0 ; } + + static wxNativePrinterDC* Create(wxPrintData* data) ; +} ; + +class wxMacCarbonPrinterDC : public wxNativePrinterDC +{ +public : + wxMacCarbonPrinterDC( wxPrintData* data ) ; + virtual ~wxMacCarbonPrinterDC() ; + virtual bool StartDoc( wxPrinterDC* dc , const wxString& message ) ; + virtual void EndDoc( wxPrinterDC* dc ) ; + virtual void StartPage( wxPrinterDC* dc ) ; + virtual void EndPage( wxPrinterDC* dc ) ; + virtual wxUint32 GetStatus() const { return m_err ; } + virtual void GetSize( int *w , int *h) const ; + virtual wxSize GetPPI() const ; +private : + wxCoord m_maxX ; + wxCoord m_maxY ; + wxSize m_ppi ; + OSStatus m_err ; +} ; + +wxMacCarbonPrinterDC::wxMacCarbonPrinterDC( wxPrintData* data ) +{ + m_err = noErr ; + wxMacCarbonPrintData *native = (wxMacCarbonPrintData*) data->GetNativeData() ; + + PMRect rPage; + m_err = PMGetAdjustedPageRect(native->m_macPageFormat, &rPage); + if ( m_err != noErr ) + return; + + m_maxX = wxCoord(rPage.right - rPage.left) ; + m_maxY = wxCoord(rPage.bottom - rPage.top); + + PMResolution res; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( PMPrinterGetOutputResolution != NULL ) + { + PMPrinter printer; + m_err = PMSessionGetCurrentPrinter(native->m_macPrintSession, &printer); + if ( m_err == noErr ) + { + m_err = PMPrinterGetOutputResolution( printer, native->m_macPrintSettings, &res) ; + if ( m_err == -9589 /* kPMKeyNotFound */ ) + { + m_err = noErr ; + res.hRes = res.vRes = 300; + } + } + } + else +#endif + { +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + m_err = PMGetResolution((PMPageFormat) (native->m_macPageFormat), &res); +#endif + } + + m_ppi = wxSize(int(res.hRes), int(res.vRes)); +} + +wxMacCarbonPrinterDC::~wxMacCarbonPrinterDC() +{ +} + +wxNativePrinterDC* wxNativePrinterDC::Create(wxPrintData* data) +{ + return new wxMacCarbonPrinterDC(data) ; +} + +bool wxMacCarbonPrinterDC::StartDoc( wxPrinterDC* dc , const wxString& WXUNUSED(message) ) +{ + if ( m_err ) + return false ; + + wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl(); + wxMacCarbonPrintData *native = (wxMacCarbonPrintData*) impl->GetPrintData().GetNativeData() ; + + m_err = PMSessionBeginCGDocumentNoDialog(native->m_macPrintSession, + native->m_macPrintSettings, + native->m_macPageFormat); + if ( m_err != noErr ) + return false; + + PMRect rPage; + m_err = PMGetAdjustedPageRect(native->m_macPageFormat, &rPage); + if ( m_err != noErr ) + return false ; + + m_maxX = wxCoord(rPage.right - rPage.left) ; + m_maxY = wxCoord(rPage.bottom - rPage.top); + + PMResolution res; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( PMPrinterGetOutputResolution != NULL ) + { + PMPrinter printer; + m_err = PMSessionGetCurrentPrinter(native->m_macPrintSession, &printer); + if ( m_err == noErr ) + { + m_err = PMPrinterGetOutputResolution( printer, native->m_macPrintSettings, &res) ; + if ( m_err == -9589 /* kPMKeyNotFound */ ) + { + m_err = noErr ; + res.hRes = res.vRes = 300; + } + } + } + else +#endif + { +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + m_err = PMGetResolution((PMPageFormat) (native->m_macPageFormat), &res); +#endif + } + + m_ppi = wxSize(int(res.hRes), int(res.vRes)); + return true ; +} + +void wxMacCarbonPrinterDC::EndDoc( wxPrinterDC* dc ) +{ + if ( m_err ) + return ; + + wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl(); + wxMacCarbonPrintData *native = (wxMacCarbonPrintData*) impl->GetPrintData().GetNativeData() ; + + m_err = PMSessionEndDocumentNoDialog(native->m_macPrintSession); +} + +void wxMacCarbonPrinterDC::StartPage( wxPrinterDC* dc ) +{ + if ( m_err ) + return ; + + wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl(); + wxMacCarbonPrintData *native = (wxMacCarbonPrintData*) impl->GetPrintData().GetNativeData() ; + + m_err = PMSessionBeginPageNoDialog(native->m_macPrintSession, + native->m_macPageFormat, + nil); + + CGContextRef pageContext; + + if ( m_err == noErr ) + { + m_err = PMSessionGetCGGraphicsContext(native->m_macPrintSession, + &pageContext ); + } + + if ( m_err != noErr ) + { + PMSessionEndPageNoDialog(native->m_macPrintSession); + PMSessionEndDocumentNoDialog(native->m_macPrintSession); + } + else + { + PMRect rPage; + + m_err = PMGetAdjustedPageRect(native->m_macPageFormat, &rPage); + if ( !m_err ) + { + PMRect paperRect ; + PMGetAdjustedPaperRect( native->m_macPageFormat , &paperRect ) ; + // make sure (0,0) is at the upper left of the printable area (wx conventions) + // Core Graphics initially has the lower left of the paper as 0,0 + CGContextTranslateCTM( pageContext , (CGFloat) -paperRect.left , (CGFloat) paperRect.bottom ) ; + CGContextScaleCTM( pageContext , 1 , -1 ) ; + } + // since this is a non-critical error, we set the flag back + m_err = noErr ; + } + impl->SetGraphicsContext( wxGraphicsContext::CreateFromNative( pageContext ) ); +} + +void wxMacCarbonPrinterDC::EndPage( wxPrinterDC* dc ) +{ + if ( m_err ) + return ; + + wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl(); + wxMacCarbonPrintData *native = (wxMacCarbonPrintData*) impl->GetPrintData().GetNativeData() ; + + m_err = PMSessionEndPageNoDialog(native->m_macPrintSession); + if ( m_err != noErr ) + { + PMSessionEndDocumentNoDialog(native->m_macPrintSession); + } + // the cg context we got when starting the page isn't valid anymore, so replace it + impl->SetGraphicsContext( wxGraphicsContext::Create() ); +} + +void wxMacCarbonPrinterDC::GetSize( int *w , int *h) const +{ + if ( w ) + *w = m_maxX ; + if ( h ) + *h = m_maxY ; +} + +wxSize wxMacCarbonPrinterDC::GetPPI() const +{ + return m_ppi ; +}; + +// +// +// + +wxPrinterDCImpl::wxPrinterDCImpl( wxPrinterDC *owner, const wxPrintData& printdata ) + : wxGCDCImpl( owner ) +{ + m_ok = false ; + m_printData = printdata ; + m_printData.ConvertToNative() ; + m_nativePrinterDC = wxNativePrinterDC::Create( &m_printData ) ; + if ( m_nativePrinterDC ) + { + m_ok = m_nativePrinterDC->Ok() ; + if ( !m_ok ) + { + wxString message ; + message.Printf( wxT("Print Error %u"), m_nativePrinterDC->GetStatus() ) ; + wxMessageDialog dialog( NULL , message , wxEmptyString, wxICON_HAND | wxOK) ; + dialog.ShowModal(); + } + else + { + wxSize sz = GetPPI(); + m_mm_to_pix_x = mm2inches * sz.x; + m_mm_to_pix_y = mm2inches * sz.y; + } + // we need at least a measuring context because people start measuring before a page + // gets printed at all + SetGraphicsContext( wxGraphicsContext::Create() ); + } +} + +wxSize wxPrinterDCImpl::GetPPI() const +{ + return m_nativePrinterDC->GetPPI() ; +} + +wxPrinterDCImpl::~wxPrinterDCImpl() +{ + delete m_nativePrinterDC ; +} + +bool wxPrinterDCImpl::StartDoc( const wxString& message ) +{ + wxASSERT_MSG( IsOk() , wxT("Called wxPrinterDC::StartDoc from an invalid object") ) ; + + if ( !m_ok ) + return false ; + + if ( m_nativePrinterDC->StartDoc( (wxPrinterDC*) GetOwner(), message ) ) + { + // in case we have to do additional things when successful + } + m_ok = m_nativePrinterDC->Ok() ; + if ( !m_ok ) + { + wxString message ; + message.Printf( wxT("Print Error %u"), m_nativePrinterDC->GetStatus() ) ; + wxMessageDialog dialog( NULL , message , wxEmptyString, wxICON_HAND | wxOK) ; + dialog.ShowModal(); + } + + return m_ok ; +} + +void wxPrinterDCImpl::EndDoc(void) +{ + if ( !m_ok ) + return ; + + m_nativePrinterDC->EndDoc( (wxPrinterDC*) GetOwner() ) ; + m_ok = m_nativePrinterDC->Ok() ; + + if ( !m_ok ) + { + wxString message ; + message.Printf( wxT("Print Error %u"), m_nativePrinterDC->GetStatus() ) ; + wxMessageDialog dialog( NULL , message , wxEmptyString, wxICON_HAND | wxOK) ; + dialog.ShowModal(); + } +} + +wxRect wxPrinterDCImpl::GetPaperRect() +{ + wxCoord w, h; + GetOwner()->GetSize(&w, &h); + wxRect pageRect(0, 0, w, h); + wxMacCarbonPrintData *native = (wxMacCarbonPrintData*) m_printData.GetNativeData() ; + OSStatus err = noErr ; + PMRect rPaper; + err = PMGetAdjustedPaperRect(native->m_macPageFormat, &rPaper); + if ( err != noErr ) + return pageRect; + return wxRect(wxCoord(rPaper.left), wxCoord(rPaper.top), + wxCoord(rPaper.right - rPaper.left), wxCoord(rPaper.bottom - rPaper.top)); +} + +void wxPrinterDCImpl::StartPage() +{ + if ( !m_ok ) + return ; + + m_logicalFunction = wxCOPY; + // m_textAlignment = wxALIGN_TOP_LEFT; + m_backgroundMode = wxTRANSPARENT; + + m_textForegroundColour = *wxBLACK; + m_textBackgroundColour = *wxWHITE; + m_pen = *wxBLACK_PEN; + m_font = *wxNORMAL_FONT; + m_brush = *wxTRANSPARENT_BRUSH; + m_backgroundBrush = *wxWHITE_BRUSH; + + m_nativePrinterDC->StartPage( (wxPrinterDC*) GetOwner() ) ; + m_ok = m_nativePrinterDC->Ok() ; + +} + +void wxPrinterDCImpl::EndPage() +{ + if ( !m_ok ) + return ; + + m_nativePrinterDC->EndPage( (wxPrinterDC*) GetOwner() ); + m_ok = m_nativePrinterDC->Ok() ; +} + +void wxPrinterDCImpl::DoGetSize(int *width, int *height) const +{ + wxCHECK_RET( m_ok , _T("GetSize() doesn't work without a valid wxPrinterDC") ); + m_nativePrinterDC->GetSize(width, height ) ; +} + +#endif diff --git a/src/osx/carbon/dcscreen.cpp b/src/osx/carbon/dcscreen.cpp new file mode 100644 index 0000000000..ad8ac0be4a --- /dev/null +++ b/src/osx/carbon/dcscreen.cpp @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dcscreen.cpp +// Purpose: wxScreenDC class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/dcscreen.h" +#include "wx/mac/carbon/dcscreen.h" + +#include "wx/mac/uma.h" +#include "wx/graphics.h" + +IMPLEMENT_ABSTRACT_CLASS(wxScreenDCImpl, wxWindowDCImpl) + +// TODO : for the Screenshot use case, which doesn't work in Quartz +// we should do a GetAsBitmap using something like +// http://www.cocoabuilder.com/archive/message/cocoa/2005/8/13/144256 + +// Create a DC representing the whole screen +wxScreenDCImpl::wxScreenDCImpl( wxDC *owner ) : + wxWindowDCImpl( owner ) +{ +#ifdef __LP64__ + m_graphicContext = NULL; + m_ok = false ; +#else + CGRect cgbounds ; + cgbounds = CGDisplayBounds(CGMainDisplayID()); + Rect bounds; + bounds.top = (short)cgbounds.origin.y; + bounds.left = (short)cgbounds.origin.x; + bounds.bottom = bounds.top + (short)cgbounds.size.height; + bounds.right = bounds.left + (short)cgbounds.size.width; + WindowAttributes overlayAttributes = kWindowIgnoreClicksAttribute; + CreateNewWindow( kOverlayWindowClass, overlayAttributes, &bounds, (WindowRef*) &m_overlayWindow ); + ShowWindow((WindowRef)m_overlayWindow); + SetGraphicsContext( wxGraphicsContext::CreateFromNativeWindow( m_overlayWindow ) ); + m_width = (wxCoord)cgbounds.size.width; + m_height = (wxCoord)cgbounds.size.height; + m_ok = true ; +#endif +} + +wxScreenDCImpl::~wxScreenDCImpl() +{ + delete m_graphicContext; + m_graphicContext = NULL; +#ifdef __LP64__ +#else + DisposeWindow((WindowRef) m_overlayWindow ); +#endif +} diff --git a/src/osx/carbon/dialog.cpp b/src/osx/carbon/dialog.cpp new file mode 100644 index 0000000000..8743e10547 --- /dev/null +++ b/src/osx/carbon/dialog.cpp @@ -0,0 +1,185 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dialog.cpp +// Purpose: wxDialog class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/dialog.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/frame.h" + #include "wx/settings.h" +#endif // WX_PRECOMP + +#include "wx/mac/uma.h" + + +// Lists to keep track of windows, so we can disable/enable them +// for modal dialogs +wxList wxModalDialogs; + +IMPLEMENT_DYNAMIC_CLASS(wxDialog, wxTopLevelWindow) + +void wxDialog::Init() +{ + m_isModalStyle = false; +} + +bool wxDialog::Create( wxWindow *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + SetExtraStyle( GetExtraStyle() | wxTOPLEVEL_EX_DIALOG ); + + // All dialogs should really have this style... + style |= wxTAB_TRAVERSAL; + + // ...but not these styles + style &= ~(wxYES | wxOK | wxNO); // | wxCANCEL + + if ( !wxTopLevelWindow::Create( parent, id, title, pos, size, style, name ) ) + return false; + + return true; +} + +void wxDialog::SetModal( bool flag ) +{ + if ( flag ) + { + m_isModalStyle = true; + + SetWindowModality( (WindowRef)MacGetWindowRef(), kWindowModalityAppModal, NULL ) ; + } + else + { + m_isModalStyle = false; + } +} + +wxDialog::~wxDialog() +{ + m_isBeingDeleted = true; + + // if the dialog is modal, this will end its event loop + Show(false); +} + +// On mac command-stop does the same thing as Esc, let the base class know +// about it +bool wxDialog::IsEscapeKey(const wxKeyEvent& event) +{ + if ( event.GetKeyCode() == '.' && event.GetModifiers() == wxMOD_CMD ) + return true; + + return wxDialogBase::IsEscapeKey(event); +} + +bool wxDialog::IsModal() const +{ + return wxModalDialogs.Find((wxDialog *)this) != NULL; // const_cast + // return m_isModalStyle; +} + + +bool wxDialog::Show(bool show) +{ + if ( !wxDialogBase::Show(show) ) + // nothing to do + return false; + + if (show && CanDoLayoutAdaptation()) + DoLayoutAdaptation(); + + if ( show ) + // usually will result in TransferDataToWindow() being called + InitDialog(); + + HiliteMenu(0); + + if ( m_isModalStyle ) + { + if ( show ) + { + DoShowModal(); + } + else // end of modal dialog + { + // this will cause IsModalShowing() return false and our local + // message loop will terminate + wxModalDialogs.DeleteObject(this); + } + } + + return true; +} + +void wxDialog::DoShowModal() +{ + wxCHECK_RET( !IsModal(), wxT("DoShowModal() called twice") ); + + wxModalDialogs.Append(this); + + SetFocus() ; + + WindowRef windowRef = (WindowRef) MacGetWindowRef(); + WindowGroupRef windowGroup; + WindowGroupRef formerParentGroup; + bool resetGroupParent = false; + + if ( GetParent() == NULL ) + { + windowGroup = GetWindowGroup(windowRef) ; + formerParentGroup = GetWindowGroupParent( windowGroup ); + SetWindowGroupParent( windowGroup, GetWindowGroupOfClass( kMovableModalWindowClass ) ); + resetGroupParent = true; + } + BeginAppModalStateForWindow(windowRef) ; + + while ( IsModal() ) + { + wxTheApp->MacDoOneEvent() ; + // calls process idle itself + } + + EndAppModalStateForWindow(windowRef) ; + if ( resetGroupParent ) + { + SetWindowGroupParent( windowGroup , formerParentGroup ); + } +} + + +// Replacement for Show(true) for modal dialogs - returns return code +int wxDialog::ShowModal() +{ + if ( !m_isModalStyle ) + SetModal(true); + + Show(true); + + return GetReturnCode(); +} + +// NB: this function (surprizingly) may be called for both modal and modeless +// dialogs and should work for both of them +void wxDialog::EndModal(int retCode) +{ + SetReturnCode(retCode); + Show(false); + SetModal(false); +} + diff --git a/src/osx/carbon/dirdlg.cpp b/src/osx/carbon/dirdlg.cpp new file mode 100644 index 0000000000..5caa7f6b4b --- /dev/null +++ b/src/osx/carbon/dirdlg.cpp @@ -0,0 +1,145 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dirdlg.cpp +// Purpose: wxDirDialog +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_DIRDLG + +#include "wx/dirdlg.h" + +#ifndef WX_PRECOMP + #include "wx/utils.h" + #include "wx/dialog.h" + #include "wx/cmndata.h" +#endif // WX_PRECOMP + +#include "wx/filename.h" + +#include "wx/mac/private.h" + +#ifdef __DARWIN__ + #include +#else + #include +#endif + +IMPLEMENT_CLASS(wxDirDialog, wxDialog) + +static pascal void NavEventProc( + NavEventCallbackMessage inSelector, + NavCBRecPtr ioParams, + NavCallBackUserData ioUserData ); + +static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc); + +static pascal void NavEventProc( + NavEventCallbackMessage inSelector, + NavCBRecPtr ioParams, + NavCallBackUserData ioUserData ) +{ + wxDirDialog * data = ( wxDirDialog *) ioUserData ; + if ( inSelector == kNavCBStart ) + { + if (data && !data->GetPath().empty() ) + { + // Set default location for the modern Navigation APIs + // Apple Technical Q&A 1151 + FSRef theFile; + wxMacPathToFSRef(data->GetPath(), &theFile); + AEDesc theLocation = { typeNull, NULL }; + if (noErr == ::AECreateDesc(typeFSRef, &theFile, sizeof(FSRef), &theLocation)) + ::NavCustomControl(ioParams->context, kNavCtlSetLocation, (void *) &theLocation); + } + } +} + +wxDirDialog::wxDirDialog(wxWindow *parent, + const wxString& message, + const wxString& defaultPath, + long WXUNUSED(style), + const wxPoint& WXUNUSED(pos), + const wxSize& WXUNUSED(size), + const wxString& WXUNUSED(name)) +{ + wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ; + m_message = message; + m_parent = parent; + m_path = defaultPath; +} + +int wxDirDialog::ShowModal() +{ + NavDialogRef dialog = NULL; + NavDialogCreationOptions options; + NavReplyRecord reply ; + bool disposeReply = false ; + OSStatus err = noErr; + + err = NavGetDefaultDialogCreationOptions(&options); + options.optionFlags &= ~kNavAllowMultipleFiles; + if (err == noErr) + { + wxCFStringRef message(m_message, GetFont().GetEncoding()); + options.message = message; + err = NavCreateChooseFolderDialog(&options, sStandardNavEventFilter , NULL, this , &dialog); + if (err == noErr) + { + err = NavDialogRun(dialog); + if ( err == noErr ) + { + err = NavDialogGetReply(dialog, &reply); + disposeReply = true ; + } + } + } + + if ( err == noErr ) + { + if ( reply.validRecord ) + { + FSRef folderInfo; + AEDesc specDesc ; + + OSErr err = ::AECoerceDesc( &reply.selection , typeFSRef, &specDesc); + if ( err != noErr ) + { + m_path = wxEmptyString ; + } + else + { + folderInfo = **(FSRef**) specDesc.dataHandle; + m_path = wxMacFSRefToPath( &folderInfo ) ; + if (specDesc.dataHandle != nil) + { + ::AEDisposeDesc(&specDesc); + } + } + } + else + { + err = paramErr ; // could be any error, only used for giving back wxID_CANCEL + } + } + + if ( disposeReply ) + ::NavDisposeReply(&reply); + + // apparently cancelling shouldn't change m_path + if ( err != noErr && err != userCanceledErr ) + m_path = wxEmptyString ; + + if ( dialog ) + ::NavDialogDispose(dialog); + + return (err == noErr) ? wxID_OK : wxID_CANCEL ; +} + +#endif diff --git a/src/osx/carbon/dirmac.cpp b/src/osx/carbon/dirmac.cpp new file mode 100644 index 0000000000..52e9a61e0f --- /dev/null +++ b/src/osx/carbon/dirmac.cpp @@ -0,0 +1,269 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: mac/dirmac.cpp +// Purpose: wxDir implementation for Mac +// Author: Stefan Csomor +// Modified by: +// Created: 08.12.99 +// RCS-ID: $Id$ +// Copyright: (c) 1999 Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/dir.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" +#endif // PCH + +#include "wx/filefn.h" // for wxDirExists() +#include "wx/filename.h" +#include "wx/mac/private.h" + +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- + +// this class stores everything we need to enumerate the files +class wxDirData +{ +public: + wxDirData(const wxString& dirname); + ~wxDirData(); + + void Close() ; + void SetFileSpec(const wxString& filespec) { m_filespec = filespec; } + void SetFlags(int flags) { m_flags = flags; } + + bool Read(wxString *filename); // reads the next + void Rewind() ; + + const wxString& GetName() const { return m_dirname; } + +private: + FSIterator m_iterator ; + + wxString m_dirname; + wxString m_filespec; + + int m_flags; +}; + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxDirData +// ---------------------------------------------------------------------------- + +wxDirData::wxDirData(const wxString& dirname) + : m_dirname(dirname) +{ + // throw away the trailing slashes + size_t n = m_dirname.length(); + wxCHECK_RET( n, _T("empty dir name in wxDir") ); + + while ( n > 0 && wxIsPathSeparator(m_dirname[--n]) ) + ; + + m_dirname.Truncate(n + 1); + m_iterator = NULL ; +} + +wxDirData::~wxDirData() +{ + Close() ; +} + +void wxDirData::Close() +{ + if ( m_iterator ) + { + FSCloseIterator( m_iterator ) ; + m_iterator = NULL ; + } +} + +void wxDirData::Rewind() +{ + Close() ; +} + +bool wxDirData::Read(wxString *filename) +{ + wxString result; + OSStatus err = noErr ; + if ( NULL == m_iterator ) + { + FSRef dirRef; + err = wxMacPathToFSRef( m_dirname , &dirRef ) ; + if ( err == noErr ) + { + err = FSOpenIterator(&dirRef, kFSIterateFlat, &m_iterator); + } + if ( err ) + { + Close() ; + return false ; + } + } + + wxString name ; + wxString lowerfilespec = m_filespec.Lower(); + + while( noErr == err ) + { + HFSUniStr255 uniname ; + FSRef fileRef; + FSCatalogInfo catalogInfo; + ItemCount fetched = 0; + + err = FSGetCatalogInfoBulk( m_iterator, 1, &fetched, NULL, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo , &catalogInfo , &fileRef, NULL, &uniname ); + + // expected error codes + + if ( errFSNoMoreItems == err ) + return false ; + if ( afpAccessDenied == err ) + return false ; + + if ( noErr != err ) + break ; + + name = wxMacHFSUniStrToString( &uniname ) ; + wxString lowername = name.Lower(); + + if ( ( name == wxT(".") || name == wxT("..") ) && !(m_flags & wxDIR_DOTDOT) ) + continue; + + if ( ( name[0U] == '.' ) && !(m_flags & wxDIR_HIDDEN ) ) + continue ; + + if ( (((FileInfo*)&catalogInfo.finderInfo)->finderFlags & kIsInvisible ) && !(m_flags & wxDIR_HIDDEN ) ) + continue ; + + // its a dir and we don't want it + if ( (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) && !(m_flags & wxDIR_DIRS) ) + continue ; + + // its a file but we don't want it + if ( (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0 && !(m_flags & wxDIR_FILES ) ) + continue ; + + if ( m_filespec.empty() || m_filespec == wxT("*.*") || m_filespec == wxT("*") ) + { + } + else if ( !wxMatchWild(lowerfilespec, lowername , false) ) + { + continue ; + } + + break ; + } + if ( err != noErr ) + { + return false ; + } + + *filename = name ; + return true; +} + +// ---------------------------------------------------------------------------- +// wxDir helpers +// ---------------------------------------------------------------------------- + +/* static */ +bool wxDir::Exists(const wxString& dir) +{ + return wxDirExists(dir); +} + +// ---------------------------------------------------------------------------- +// wxDir construction/destruction +// ---------------------------------------------------------------------------- + +wxDir::wxDir(const wxString& dirname) +{ + m_data = NULL; + + (void)Open(dirname); +} + +bool wxDir::Open(const wxString& dirname) +{ + delete m_data; + m_data = new wxDirData(dirname); + + return true; +} + +bool wxDir::IsOpened() const +{ + return m_data != NULL; +} + +wxString wxDir::GetName() const +{ + wxString name; + if ( m_data ) + { + name = m_data->GetName(); + if ( !name.empty() && (name.Last() == _T('/')) ) + { + // chop off the last (back)slash + name.Truncate(name.length() - 1); + } + } + + return name; +} + +wxDir::~wxDir() +{ + delete m_data; + m_data = NULL; +} + +// ---------------------------------------------------------------------------- +// wxDir enumerating +// ---------------------------------------------------------------------------- + +bool wxDir::GetFirst(wxString *filename, + const wxString& filespec, + int flags) const +{ + wxCHECK_MSG( IsOpened(), false, _T("must wxDir::Open() first") ); + + m_data->Rewind(); + + m_data->SetFileSpec(filespec); + m_data->SetFlags(flags); + + return GetNext(filename); +} + +bool wxDir::GetNext(wxString *filename) const +{ + wxCHECK_MSG( IsOpened(), false, _T("must wxDir::Open() first") ); + + wxCHECK_MSG( filename, false, _T("bad pointer in wxDir::GetNext()") ); + + return m_data->Read(filename); +} diff --git a/src/osx/carbon/display.cpp b/src/osx/carbon/display.cpp new file mode 100644 index 0000000000..d8159cb352 --- /dev/null +++ b/src/osx/carbon/display.cpp @@ -0,0 +1,255 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/display.cpp +// Purpose: Mac implementation of wxDisplay class +// Author: Ryan Norton & Brian Victor +// Modified by: Royce Mitchell III, Vadim Zeitlin +// Created: 06/21/02 +// RCS-ID: $Id$ +// Copyright: (c) wxWidgets team +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_DISPLAY + +#include "wx/display.h" + +#ifndef WX_PRECOMP + #include "wx/dynarray.h" + #include "wx/log.h" + #include "wx/string.h" + #include "wx/gdicmn.h" +#endif + +#include + +#include "wx/display_impl.h" + +// ---------------------------------------------------------------------------- +// display classes implementation +// ---------------------------------------------------------------------------- + +class wxDisplayImplMacOSX : public wxDisplayImpl +{ +public: + wxDisplayImplMacOSX(unsigned n, CGDirectDisplayID id) + : wxDisplayImpl(n), + m_id(id) + { + } + + virtual wxRect GetGeometry() const; + virtual wxRect GetClientArea() const; + virtual wxString GetName() const { return wxString(); } + + virtual wxArrayVideoModes GetModes(const wxVideoMode& mode) const; + virtual wxVideoMode GetCurrentMode() const; + virtual bool ChangeMode(const wxVideoMode& mode); + +private: + CGDirectDisplayID m_id; + + DECLARE_NO_COPY_CLASS(wxDisplayImplMacOSX) +}; + +class wxDisplayFactoryMacOSX : public wxDisplayFactory +{ +public: + wxDisplayFactoryMacOSX() {} + + virtual wxDisplayImpl *CreateDisplay(unsigned n); + virtual unsigned GetCount(); + virtual int GetFromPoint(const wxPoint& pt); + +protected: + DECLARE_NO_COPY_CLASS(wxDisplayFactoryMacOSX) +}; + +// ============================================================================ +// wxDisplayFactoryMacOSX implementation +// ============================================================================ + +unsigned wxDisplayFactoryMacOSX::GetCount() +{ + CGDisplayCount count; +#ifdef __WXDEBUG__ + CGDisplayErr err = +#endif + CGGetActiveDisplayList(0, NULL, &count); + + wxASSERT(err == CGDisplayNoErr); + + return count; +} + +int wxDisplayFactoryMacOSX::GetFromPoint(const wxPoint& p) +{ + CGPoint thePoint = {(float)p.x, (float)p.y}; + CGDirectDisplayID theID; + CGDisplayCount theCount; + CGDisplayErr err = CGGetDisplaysWithPoint(thePoint, 1, &theID, &theCount); + wxASSERT(err == CGDisplayNoErr); + + int nWhich = wxNOT_FOUND; + + if (theCount) + { + theCount = GetCount(); + CGDirectDisplayID* theIDs = new CGDirectDisplayID[theCount]; + err = CGGetActiveDisplayList(theCount, theIDs, &theCount); + wxASSERT(err == CGDisplayNoErr); + + for (nWhich = 0; nWhich < (int) theCount; ++nWhich) + { + if (theIDs[nWhich] == theID) + break; + } + + delete [] theIDs; + + if (nWhich == (int) theCount) + { + wxFAIL_MSG(wxT("Failed to find display in display list")); + nWhich = wxNOT_FOUND; + } + } + + return nWhich; +} + +wxDisplayImpl *wxDisplayFactoryMacOSX::CreateDisplay(unsigned n) +{ + CGDisplayCount theCount = GetCount(); + CGDirectDisplayID* theIDs = new CGDirectDisplayID[theCount]; + +#ifdef __WXDEBUG__ + CGDisplayErr err = +#endif + CGGetActiveDisplayList(theCount, theIDs, &theCount); + + wxASSERT( err == CGDisplayNoErr ); + wxASSERT( n < theCount ); + + wxDisplayImplMacOSX *display = new wxDisplayImplMacOSX(n, theIDs[n]); + + delete [] theIDs; + + return display; +} + +// ============================================================================ +// wxDisplayImplMacOSX implementation +// ============================================================================ + +wxRect wxDisplayImplMacOSX::GetGeometry() const +{ + CGRect theRect = CGDisplayBounds(m_id); + return wxRect( (int)theRect.origin.x, + (int)theRect.origin.y, + (int)theRect.size.width, + (int)theRect.size.height ); //floats +} + +wxRect wxDisplayImplMacOSX::GetClientArea() const +{ + // VZ: I don't know how to get client area for arbitrary display but + // wxGetClientDisplayRect() does work correctly for at least the main + // one (TODO: do it correctly for the other displays too) + if ( IsPrimary() ) + return wxGetClientDisplayRect(); + + return wxDisplayImpl::GetClientArea(); +} + +static int wxCFDictKeyToInt( CFDictionaryRef desc, CFStringRef key ) +{ + CFNumberRef value = (CFNumberRef) CFDictionaryGetValue( desc, key ); + if (value == NULL) + return 0; + + int num = 0; + CFNumberGetValue( value, kCFNumberIntType, &num ); + + return num; +} + +wxArrayVideoModes wxDisplayImplMacOSX::GetModes(const wxVideoMode& mode) const +{ + wxArrayVideoModes resultModes; + + CFArrayRef theArray = CGDisplayAvailableModes( m_id ); + + for (CFIndex i = 0; i < CFArrayGetCount(theArray); ++i) + { + CFDictionaryRef theValue = (CFDictionaryRef) CFArrayGetValueAtIndex( theArray, i ); + + wxVideoMode theMode( + wxCFDictKeyToInt( theValue, kCGDisplayWidth ), + wxCFDictKeyToInt( theValue, kCGDisplayHeight ), + wxCFDictKeyToInt( theValue, kCGDisplayBitsPerPixel ), + wxCFDictKeyToInt( theValue, kCGDisplayRefreshRate )); + + if (theMode.Matches( mode )) + resultModes.Add( theMode ); + } + + return resultModes; +} + +wxVideoMode wxDisplayImplMacOSX::GetCurrentMode() const +{ + CFDictionaryRef theValue = CGDisplayCurrentMode( m_id ); + + return wxVideoMode( + wxCFDictKeyToInt( theValue, kCGDisplayWidth ), + wxCFDictKeyToInt( theValue, kCGDisplayHeight ), + wxCFDictKeyToInt( theValue, kCGDisplayBitsPerPixel ), + wxCFDictKeyToInt( theValue, kCGDisplayRefreshRate )); +} + +bool wxDisplayImplMacOSX::ChangeMode( const wxVideoMode& mode ) +{ + // Changing to default mode (wxDefaultVideoMode) doesn't + // work because we don't have access to the system's 'scrn' + // resource which holds the user's mode which the system + // will return to after this app is done + boolean_t bExactMatch; + CFDictionaryRef theCGMode = CGDisplayBestModeForParametersAndRefreshRate( + m_id, + (size_t)mode.GetDepth(), + (size_t)mode.GetWidth(), + (size_t)mode.GetHeight(), + (double)mode.GetRefresh(), + &bExactMatch ); + + bool bOK = bExactMatch; + + if (bOK) + bOK = CGDisplaySwitchToMode( m_id, theCGMode ) == CGDisplayNoErr; + + return bOK; +} + +// ============================================================================ +// wxDisplay::CreateFactory() +// ============================================================================ + +/* static */ wxDisplayFactory *wxDisplay::CreateFactory() +{ + return new wxDisplayFactoryMacOSX; +} + +#endif // wxUSE_DISPLAY diff --git a/src/osx/carbon/dnd.cpp b/src/osx/carbon/dnd.cpp new file mode 100644 index 0000000000..11709e12ce --- /dev/null +++ b/src/osx/carbon/dnd.cpp @@ -0,0 +1,562 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dnd.cpp +// Purpose: wxDropTarget, wxDropSource implementations +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) 1998 Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_DRAG_AND_DROP + +#include "wx/dnd.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/toplevel.h" + #include "wx/gdicmn.h" +#endif // WX_PRECOMP + +#include "wx/mac/private.h" + +// ---------------------------------------------------------------------------- +// globals +// ---------------------------------------------------------------------------- + +typedef struct +{ + wxWindow *m_currentTargetWindow; + wxDropTarget *m_currentTarget; + wxDropSource *m_currentSource; + wxDragResult m_result; + int m_flags; +} MacTrackingGlobals; + +MacTrackingGlobals gTrackingGlobals; + +void wxMacEnsureTrackingHandlersInstalled(); + +//---------------------------------------------------------------------------- +// wxDropTarget +//---------------------------------------------------------------------------- + +wxDropTarget::wxDropTarget( wxDataObject *data ) + : wxDropTargetBase( data ) +{ + wxMacEnsureTrackingHandlersInstalled(); +} + +wxDragResult wxDropTarget::OnDragOver( + wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), + wxDragResult def ) +{ + return CurrentDragHasSupportedFormat() ? def : wxDragNone; +} + +bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) ) +{ + if (m_dataObject == NULL) + return false; + + return CurrentDragHasSupportedFormat(); +} + +wxDragResult wxDropTarget::OnData( + wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), + wxDragResult def ) +{ + if (m_dataObject == NULL) + return wxDragNone; + + if (!CurrentDragHasSupportedFormat()) + return wxDragNone; + + return GetData() ? def : wxDragNone; +} + +bool wxDropTarget::CurrentDragHasSupportedFormat() +{ + bool supported = false; + if (m_dataObject == NULL) + return false; + + if ( gTrackingGlobals.m_currentSource != NULL ) + { + wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject(); + + if ( data ) + { + size_t formatcount = data->GetFormatCount(); + wxDataFormat *array = new wxDataFormat[formatcount]; + data->GetAllFormats( array ); + for (size_t i = 0; !supported && i < formatcount; i++) + { + wxDataFormat format = array[i]; + if ( m_dataObject->IsSupported( format ) ) + { + supported = true; + break; + } + } + + delete [] array; + } + } + + if ( !supported ) + { + PasteboardRef pasteboard; + + if ( GetDragPasteboard( (DragReference)m_currentDrag, &pasteboard ) == noErr ) + { + supported = m_dataObject->HasDataInPasteboard( pasteboard ); + } + } + + return supported; +} + +bool wxDropTarget::GetData() +{ + if (m_dataObject == NULL) + return false; + + if ( !CurrentDragHasSupportedFormat() ) + return false; + + bool transferred = false; + if ( gTrackingGlobals.m_currentSource != NULL ) + { + wxDataObject* data = gTrackingGlobals.m_currentSource->GetDataObject(); + + if (data != NULL) + { + size_t formatcount = data->GetFormatCount(); + wxDataFormat *array = new wxDataFormat[formatcount]; + data->GetAllFormats( array ); + for (size_t i = 0; !transferred && i < formatcount; i++) + { + wxDataFormat format = array[i]; + if ( m_dataObject->IsSupported( format ) ) + { + int size = data->GetDataSize( format ); + transferred = true; + + if (size == 0) + { + m_dataObject->SetData( format, 0, 0 ); + } + else + { + char *d = new char[size]; + data->GetDataHere( format, (void*)d ); + m_dataObject->SetData( format, size, d ); + delete [] d; + } + } + } + + delete [] array; + } + } + + if ( !transferred ) + { + PasteboardRef pasteboard; + + if ( GetDragPasteboard( (DragReference)m_currentDrag, &pasteboard ) == noErr ) + { + transferred = m_dataObject->GetFromPasteboard( pasteboard ); + } + } + + return transferred; +} + +//------------------------------------------------------------------------- +// wxDropSource +//------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// drag request + +wxDropSource::wxDropSource(wxWindow *win, + const wxCursor &cursorCopy, + const wxCursor &cursorMove, + const wxCursor &cursorStop) + : wxDropSourceBase(cursorCopy, cursorMove, cursorStop) +{ + wxMacEnsureTrackingHandlersInstalled(); + + m_window = win; +} + +wxDropSource::wxDropSource(wxDataObject& data, + wxWindow *win, + const wxCursor &cursorCopy, + const wxCursor &cursorMove, + const wxCursor &cursorStop) + : wxDropSourceBase(cursorCopy, cursorMove, cursorStop) +{ + wxMacEnsureTrackingHandlersInstalled(); + + SetData( data ); + m_window = win; +} + +wxDropSource::~wxDropSource() +{ +} + +OSStatus wxMacPromiseKeeper(PasteboardRef WXUNUSED(inPasteboard), + PasteboardItemID WXUNUSED(inItem), + CFStringRef WXUNUSED(inFlavorType), + void * WXUNUSED(inContext)) +{ + OSStatus err = noErr; + + // we might add promises here later, inContext is the wxDropSource* + + return err; +} + +wxDragResult wxDropSource::DoDragDrop(int flags) +{ + wxASSERT_MSG( m_data, wxT("Drop source: no data") ); + + if ((m_data == NULL) || (m_data->GetFormatCount() == 0)) + return (wxDragResult)wxDragNone; + + DragReference theDrag; + RgnHandle dragRegion; + OSStatus err = noErr; + PasteboardRef pasteboard; + + // add data to drag + + err = PasteboardCreate( kPasteboardUniqueName, &pasteboard ); + if ( err != noErr ) + return wxDragNone; + + // we add a dummy promise keeper because of strange messages when linking against carbon debug + err = PasteboardSetPromiseKeeper( pasteboard, wxMacPromiseKeeper, this ); + if ( err != noErr ) + { + CFRelease( pasteboard ); + return wxDragNone; + } + + err = PasteboardClear( pasteboard ); + if ( err != noErr ) + { + CFRelease( pasteboard ); + return wxDragNone; + } + PasteboardSynchronize( pasteboard ); + + m_data->AddToPasteboard( pasteboard, 1 ); + + if (NewDragWithPasteboard( pasteboard , &theDrag) != noErr) + { + CFRelease( pasteboard ); + return wxDragNone; + } + + dragRegion = NewRgn(); + RgnHandle tempRgn = NewRgn(); + + EventRecord rec; + ConvertEventRefToEventRecord( (EventRef) wxTheApp->MacGetCurrentEvent(), &rec ); + + const short dragRegionOuterBoundary = 10; + const short dragRegionInnerBoundary = 9; + + SetRectRgn( + dragRegion, + rec.where.h - dragRegionOuterBoundary, + rec.where.v - dragRegionOuterBoundary, + rec.where.h + dragRegionOuterBoundary, + rec.where.v + dragRegionOuterBoundary ); + + SetRectRgn( + tempRgn, + rec.where.h - dragRegionInnerBoundary, + rec.where.v - dragRegionInnerBoundary, + rec.where.h + dragRegionInnerBoundary, + rec.where.v + dragRegionInnerBoundary ); + + DiffRgn( dragRegion, tempRgn, dragRegion ); + DisposeRgn( tempRgn ); + + // TODO: work with promises in order to return data + // only when drag was successfully completed + + gTrackingGlobals.m_currentSource = this; + gTrackingGlobals.m_result = wxDragNone; + gTrackingGlobals.m_flags = flags; + + err = TrackDrag( theDrag, &rec, dragRegion ); + + DisposeRgn( dragRegion ); + DisposeDrag( theDrag ); + CFRelease( pasteboard ); + gTrackingGlobals.m_currentSource = NULL; + + return gTrackingGlobals.m_result; +} + +bool wxDropSource::MacInstallDefaultCursor(wxDragResult effect) +{ + const wxCursor& cursor = GetCursor(effect); + bool result = cursor.Ok(); + + if ( result ) + cursor.MacInstall(); + + return result; +} + +bool gTrackingGlobalsInstalled = false; + +// passing the globals via refcon is not needed by the CFM and later architectures anymore +// but I'll leave it in there, just in case... + +pascal OSErr wxMacWindowDragTrackingHandler( + DragTrackingMessage theMessage, WindowPtr theWindow, + void *handlerRefCon, DragReference theDrag ); +pascal OSErr wxMacWindowDragReceiveHandler( + WindowPtr theWindow, void *handlerRefCon, + DragReference theDrag ); + +void wxMacEnsureTrackingHandlersInstalled() +{ + if ( !gTrackingGlobalsInstalled ) + { + OSStatus err; + + err = InstallTrackingHandler( NewDragTrackingHandlerUPP(wxMacWindowDragTrackingHandler), 0L, &gTrackingGlobals ); + verify_noerr( err ); + + err = InstallReceiveHandler( NewDragReceiveHandlerUPP(wxMacWindowDragReceiveHandler), 0L, &gTrackingGlobals ); + verify_noerr( err ); + + gTrackingGlobalsInstalled = true; + } +} + +pascal OSErr wxMacWindowDragTrackingHandler( + DragTrackingMessage theMessage, WindowPtr theWindow, + void *handlerRefCon, DragReference theDrag ) +{ + MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*) handlerRefCon; + + Point mouse, localMouse; + DragAttributes attributes; + + GetDragAttributes( theDrag, &attributes ); + + wxNonOwnedWindow* toplevel = wxFindWinFromMacWindow( theWindow ); + + bool optionDown = GetCurrentKeyModifiers() & optionKey; + wxDragResult result = optionDown ? wxDragCopy : wxDragMove; + + switch (theMessage) + { + case kDragTrackingEnterHandler: + case kDragTrackingLeaveHandler: + break; + + case kDragTrackingEnterWindow: + if (trackingGlobals != NULL) + { + trackingGlobals->m_currentTargetWindow = NULL; + trackingGlobals->m_currentTarget = NULL; + } + break; + + case kDragTrackingInWindow: + if (trackingGlobals == NULL) + break; + if (toplevel == NULL) + break; + + GetDragMouse( theDrag, &mouse, 0L ); + localMouse = mouse; + wxMacGlobalToLocal( theWindow, &localMouse ); + + { + wxWindow *win = NULL; + ControlPartCode controlPart; + ControlRef control = FindControlUnderMouse( localMouse, theWindow, &controlPart ); + if ( control ) + win = wxFindControlFromMacControl( control ); + else + win = toplevel; + + int localx, localy; + localx = localMouse.h; + localy = localMouse.v; + + if ( win ) + win->MacRootWindowToWindow( &localx, &localy ); + if ( win != trackingGlobals->m_currentTargetWindow ) + { + if ( trackingGlobals->m_currentTargetWindow ) + { + // this window is left + if ( trackingGlobals->m_currentTarget ) + { +#ifndef __LP64__ + HideDragHilite( theDrag ); +#endif + trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); + trackingGlobals->m_currentTarget->OnLeave(); + trackingGlobals->m_currentTarget = NULL; + trackingGlobals->m_currentTargetWindow = NULL; + } + } + + if ( win ) + { + // this window is entered + trackingGlobals->m_currentTargetWindow = win; + trackingGlobals->m_currentTarget = win->GetDropTarget(); + { + if ( trackingGlobals->m_currentTarget ) + { + trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); + result = trackingGlobals->m_currentTarget->OnEnter( localx, localy, result ); + } + + if ( result != wxDragNone ) + { + int x, y; + + x = y = 0; + win->MacWindowToRootWindow( &x, &y ); + RgnHandle hiliteRgn = NewRgn(); + Rect r = { y, x, y + win->GetSize().y, x + win->GetSize().x }; + RectRgn( hiliteRgn, &r ); +#ifndef __LP64__ + ShowDragHilite( theDrag, hiliteRgn, true ); +#endif + DisposeRgn( hiliteRgn ); + } + } + } + } + else + { + if ( trackingGlobals->m_currentTarget ) + { + trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); + result = trackingGlobals->m_currentTarget->OnDragOver( localx, localy, result ); + } + } + + // set cursor for OnEnter and OnDragOver + if ( trackingGlobals->m_currentSource && !trackingGlobals->m_currentSource->GiveFeedback( result ) ) + { + if ( !trackingGlobals->m_currentSource->MacInstallDefaultCursor( result ) ) + { + int cursorID = wxCURSOR_NONE; + + switch (result) + { + case wxDragCopy: + cursorID = wxCURSOR_COPY_ARROW; + break; + + case wxDragMove: + cursorID = wxCURSOR_ARROW; + break; + + case wxDragNone: + cursorID = wxCURSOR_NO_ENTRY; + break; + + case wxDragError: + case wxDragLink: + case wxDragCancel: + default: + // put these here to make gcc happy + ; + } + + if (cursorID != wxCURSOR_NONE) + { + wxCursor cursor( cursorID ); + cursor.MacInstall(); + } + } + } + } + break; + + case kDragTrackingLeaveWindow: + if (trackingGlobals == NULL) + break; + + if (trackingGlobals->m_currentTarget) + { + trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); + trackingGlobals->m_currentTarget->OnLeave(); +#ifndef __LP64__ + HideDragHilite( theDrag ); +#endif + trackingGlobals->m_currentTarget = NULL; + } + trackingGlobals->m_currentTargetWindow = NULL; + break; + + default: + break; + } + + return noErr; +} + +pascal OSErr wxMacWindowDragReceiveHandler( + WindowPtr theWindow, + void *handlerRefCon, + DragReference theDrag) +{ + MacTrackingGlobals* trackingGlobals = (MacTrackingGlobals*)handlerRefCon; + if ( trackingGlobals->m_currentTarget ) + { + Point mouse, localMouse; + int localx, localy; + + trackingGlobals->m_currentTarget->SetCurrentDrag( theDrag ); + GetDragMouse( theDrag, &mouse, 0L ); + localMouse = mouse; + wxMacGlobalToLocal( theWindow, &localMouse ); + localx = localMouse.h; + localy = localMouse.v; + + // TODO : should we use client coordinates? + if ( trackingGlobals->m_currentTargetWindow ) + trackingGlobals->m_currentTargetWindow->MacRootWindowToWindow( &localx, &localy ); + if ( trackingGlobals->m_currentTarget->OnDrop( localx, localy ) ) + { + // the option key indicates copy in Mac UI, if it's not pressed do + // move by default if it's allowed at all + wxDragResult + result = !(trackingGlobals->m_flags & wxDrag_AllowMove) || + (GetCurrentKeyModifiers() & optionKey) + ? wxDragCopy + : wxDragMove; + trackingGlobals->m_result = + trackingGlobals->m_currentTarget->OnData( localx, localy, result ); + } + } + + return noErr; +} + +#endif // wxUSE_DRAG_AND_DROP + diff --git a/src/osx/carbon/drawer.cpp b/src/osx/carbon/drawer.cpp new file mode 100644 index 0000000000..4adb28e239 --- /dev/null +++ b/src/osx/carbon/drawer.cpp @@ -0,0 +1,199 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: drawer.cpp +// Purpose: Drawer child window classes. +// Drawer windows appear under their parent window and +// behave like a drawer, opening and closing to reveal +// content that does not need to be visible at all times. +// Author: Jason Bagley +// Modified by: Ryan Norton (To make it work :), plus bug fixes) +// Created: 2004-30-01 +// RCS-ID: $Id$ +// Copyright: (c) Jason Bagley; Art & Logic, Inc. +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/mac/private.h" + +#if defined( __WXMAC__ ) + +#include "wx/mac/carbon/drawer.h" + +IMPLEMENT_DYNAMIC_CLASS(wxDrawerWindow, wxWindow) + +// Use constants for now. +// These can be made into member variables and set dynamically. +const int kLeadingOffset = 20; +const int kTrailingOffset = 20; + + +// Converts Mac window edge constants to wxDirections, wxLEFT, wxRIGHT, etc. +static wxDirection WindowEdgeToDirection(OptionBits edge); + +// Convert wxDirections to MAc window edge constants. +static OptionBits DirectionToWindowEdge(wxDirection direction); + + +wxDrawerWindow::wxDrawerWindow() +{ +} + +wxDrawerWindow::~wxDrawerWindow() +{ + m_isBeingDeleted = TRUE; + this->Show(FALSE); +} + +bool wxDrawerWindow::Create(wxWindow *parent, + wxWindowID id, const wxString& title, + wxSize size, wxDirection edge, const wxString& name) +{ + wxASSERT_MSG(NULL != parent, wxT("wxDrawerWindows must be attached to a parent window.")); + + // Constrain the drawer size to the parent window. + const wxSize parentSize(parent->GetClientSize()); + if (wxLEFT == edge || wxRIGHT == edge) + { + if (size.GetHeight() > parentSize.GetHeight()) + size.SetHeight(parentSize.GetHeight() - (kLeadingOffset + kTrailingOffset)); + } + else + { + if (size.GetWidth() > parentSize.GetWidth()) + size.SetWidth(parentSize.GetWidth() - (kLeadingOffset + kTrailingOffset)); + } + + // Create the drawer window. + const wxPoint pos(0, 0); + const wxSize dummySize(0,0); + const long style = wxFRAME_DRAWER; + + bool success = wxWindow::Create(parent, id, pos, dummySize, style, name); + if (success) + { + this->MacCreateRealWindow(pos, size, style, name); + success = (m_macWindow != NULL); + } + + if (success) + { + // Use drawer brush. + SetBackgroundColour( wxColour( wxMacCreateCGColorFromHITheme( kThemeBrushDrawerBackground ) ) ); + ::SetThemeWindowBackground((WindowRef)m_macWindow, kThemeBrushDrawerBackground, false); + + // Leading and trailing offset are gaps from parent window edges + // to where the drawer starts. + ::SetDrawerOffsets((WindowRef)m_macWindow, kLeadingOffset, kTrailingOffset); + + // Set the drawers parent. + // Is there a better way to get the parent's WindowRef? + wxTopLevelWindow* tlwParent = wxDynamicCast(parent, wxTopLevelWindow); + if (NULL != tlwParent) + { + OSStatus status = ::SetDrawerParent((WindowRef)m_macWindow, + (WindowRef)tlwParent->MacGetWindowRef()); + success = (noErr == status); + } + else + success = false; + } + + return success && SetPreferredEdge(edge); +} + +wxDirection wxDrawerWindow::GetCurrentEdge() const +{ + const OptionBits edge = ::GetDrawerCurrentEdge((WindowRef)m_macWindow); + return WindowEdgeToDirection(edge); +} + +wxDirection wxDrawerWindow::GetPreferredEdge() const +{ + const OptionBits edge = ::GetDrawerPreferredEdge((WindowRef)m_macWindow); + return WindowEdgeToDirection(edge); +} + +bool wxDrawerWindow::IsOpen() const +{ + WindowDrawerState state = ::GetDrawerState((WindowRef)m_macWindow); + return (state == kWindowDrawerOpen || state == kWindowDrawerOpening); +} + +bool wxDrawerWindow::Open(bool show) +{ + static const Boolean kAsynchronous = true; + OSStatus status = noErr; + + if (show) + { + const OptionBits preferredEdge = ::GetDrawerPreferredEdge((WindowRef)m_macWindow); + status = ::OpenDrawer((WindowRef)m_macWindow, preferredEdge, kAsynchronous); + } + else + status = ::CloseDrawer((WindowRef)m_macWindow, kAsynchronous); + + return (noErr == status); +} + +bool wxDrawerWindow::SetPreferredEdge(wxDirection edge) +{ + const OSStatus status = ::SetDrawerPreferredEdge((WindowRef)m_macWindow, + DirectionToWindowEdge(edge)); + return (noErr == status); +} + + +OptionBits DirectionToWindowEdge(wxDirection direction) +{ + OptionBits edge; + switch (direction) + { + case wxTOP: + edge = kWindowEdgeTop; + break; + + case wxBOTTOM: + edge = kWindowEdgeBottom; + break; + + case wxRIGHT: + edge = kWindowEdgeRight; + break; + + case wxLEFT: + default: + edge = kWindowEdgeLeft; + break; + } + return edge; +} + +wxDirection WindowEdgeToDirection(OptionBits edge) +{ + wxDirection direction; + switch (edge) + { + case kWindowEdgeTop: + direction = wxTOP; + break; + + case kWindowEdgeBottom: + direction = wxBOTTOM; + break; + + case kWindowEdgeRight: + direction = wxRIGHT; + break; + + case kWindowEdgeDefault: // store current preferred and return that here? + case kWindowEdgeLeft: + default: + direction = wxLEFT; + break; + } + + return direction; +} + +#endif // defined( __WXMAC__ ) diff --git a/src/osx/carbon/dummy.txt b/src/osx/carbon/dummy.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/osx/carbon/evtloop.cpp b/src/osx/carbon/evtloop.cpp new file mode 100644 index 0000000000..681564bffa --- /dev/null +++ b/src/osx/carbon/evtloop.cpp @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/evtloop.cpp +// Purpose: implementation of wxEventLoop for wxMac +// Author: Vadim Zeitlin +// Modified by: +// Created: 2006-01-12 +// RCS-ID: $Id$ +// Copyright: (c) 2006 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// for compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/evtloop.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" +#endif // WX_PRECOMP + +#ifdef __DARWIN__ + #include +#else + #include +#endif +// ============================================================================ +// wxEventLoop implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// high level functions for RunApplicationEventLoop() case +// ---------------------------------------------------------------------------- + +#if wxMAC_USE_RUN_APP_EVENT_LOOP + +int wxGUIEventLoop::Run() +{ + wxEventLoopActivator activate(this); + + RunApplicationEventLoop(); + + return m_exitcode; +} + +void wxGUIEventLoop::Exit(int rc) +{ + m_exitcode = rc; + + QuitApplicationEventLoop(); + + OnExit(); +} + +#else // manual event loop + +// ---------------------------------------------------------------------------- +// functions only used by wxEventLoopManual-based implementation +// ---------------------------------------------------------------------------- + +void wxGUIEventLoop::WakeUp() +{ + extern void wxMacWakeUp(); + + wxMacWakeUp(); +} + +#endif // high/low-level event loop + +// ---------------------------------------------------------------------------- +// low level functions used in both cases +// ---------------------------------------------------------------------------- + +bool wxGUIEventLoop::Pending() const +{ + EventRef theEvent; + + return ReceiveNextEvent + ( + 0, // we want any event at all so we don't specify neither + NULL, // the number of event types nor the types themselves + kEventDurationNoWait, + false, // don't remove the event from queue + &theEvent + ) == noErr; +} + +bool wxGUIEventLoop::Dispatch() +{ + // TODO: we probably should do the dispatching directly from here but for + // now it's easier to forward to wxApp which has all the code to do + // it + if ( !wxTheApp ) + return false; + + wxTheApp->MacDoOneEvent(); + return true; +} diff --git a/src/osx/carbon/filedlg.cpp b/src/osx/carbon/filedlg.cpp new file mode 100644 index 0000000000..6e43845335 --- /dev/null +++ b/src/osx/carbon/filedlg.cpp @@ -0,0 +1,436 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/filedlg.cpp +// Purpose: wxFileDialog +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_FILEDLG + +#include "wx/filedlg.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/dialog.h" +#endif + +#include "wx/tokenzr.h" +#include "wx/filename.h" + +#include "wx/mac/private.h" + +#ifndef __DARWIN__ + #include + #include "PLStringFuncs.h" +#endif + +IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase) + +// the data we need to pass to our standard file hook routine +// includes a pointer to the dialog, a pointer to the standard +// file reply record (so we can inspect the current selection) +// and a copy of the "previous" file spec of the reply record +// so we can see if the selection has changed + +struct OpenUserDataRec +{ + int currentfilter ; + bool saveMode ; + wxArrayString name ; + wxArrayString extensions ; + wxArrayLong filtermactypes ; + wxString defaultLocation; + CFArrayRef menuitems ; +}; + +typedef struct OpenUserDataRec +OpenUserDataRec, *OpenUserDataRecPtr; + +static pascal void NavEventProc( + NavEventCallbackMessage inSelector, + NavCBRecPtr ioParams, + NavCallBackUserData ioUserData ); + +static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc); + +static pascal void NavEventProc( + NavEventCallbackMessage inSelector, + NavCBRecPtr ioParams, + NavCallBackUserData ioUserData ) +{ + OpenUserDataRec * data = ( OpenUserDataRec *) ioUserData ; + if (inSelector == kNavCBEvent) + { + } + else if ( inSelector == kNavCBStart ) + { + if (data && !(data->defaultLocation).empty()) + { + // Set default location for the modern Navigation APIs + // Apple Technical Q&A 1151 + FSRef theFile; + wxMacPathToFSRef(data->defaultLocation, &theFile); + AEDesc theLocation = { typeNull, NULL }; + if (noErr == ::AECreateDesc(typeFSRef, &theFile, sizeof(FSRef), &theLocation)) + ::NavCustomControl(ioParams->context, kNavCtlSetLocation, (void *) &theLocation); + } + + if( data->extensions.GetCount() > 0 ) + { + NavMenuItemSpec menuItem; + memset( &menuItem, 0, sizeof(menuItem) ); + menuItem.version = kNavMenuItemSpecVersion; + menuItem.menuType = data->currentfilter; + ::NavCustomControl(ioParams->context, kNavCtlSelectCustomType, &menuItem); + } + } + else if ( inSelector == kNavCBPopupMenuSelect ) + { + NavMenuItemSpec * menu = (NavMenuItemSpec *) ioParams->eventData.eventDataParms.param ; + const size_t numFilters = data->extensions.GetCount(); + + if ( menu->menuType < numFilters ) + { + data->currentfilter = menu->menuType ; + if ( data->saveMode ) + { + int i = menu->menuType ; + + // isolate the first extension string + wxString firstExtension = data->extensions[i].BeforeFirst('|').BeforeFirst(';'); + + wxString extension = firstExtension.AfterLast('.') ; + wxString sfilename ; + + wxCFStringRef cfString( wxCFRetain( NavDialogGetSaveFileName( ioParams->context ) ) ); + sfilename = cfString.AsString() ; + + int pos = sfilename.Find('.', true) ; + if ( pos != wxNOT_FOUND && extension != wxT("*") ) + { + sfilename = sfilename.Left(pos+1)+extension ; + cfString = wxCFStringRef( sfilename , wxFONTENCODING_DEFAULT ) ; + NavDialogSetSaveFileName( ioParams->context , cfString ) ; + } + } + } + } +} + +void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter ) +{ + myData->menuitems = NULL ; + myData->currentfilter = 0 ; + myData->saveMode = false ; + + if ( !filter.empty() ) + { + wxString filter2(filter) ; + int filterIndex = 0; + bool isName = true ; + wxString current ; + + for ( unsigned int i = 0; i < filter2.length() ; i++ ) + { + if ( filter2.GetChar(i) == wxT('|') ) + { + if ( isName ) + { + myData->name.Add( current ) ; + } + else + { + myData->extensions.Add( current ) ; + ++filterIndex ; + } + + isName = !isName ; + current = wxEmptyString ; + } + else + { + current += filter2.GetChar(i) ; + } + } + // we allow for compatibility reason to have a single filter expression (like *.*) without + // an explanatory text, in that case the first part is name and extension at the same time + + wxASSERT_MSG( filterIndex == 0 || !isName , wxT("incorrect format of format string") ) ; + if ( current.empty() ) + myData->extensions.Add( myData->name[filterIndex] ) ; + else + myData->extensions.Add( current ) ; + if ( filterIndex == 0 || isName ) + myData->name.Add( current ) ; + + ++filterIndex ; + + const size_t extCount = myData->extensions.GetCount(); + for ( size_t i = 0 ; i < extCount; i++ ) + { + wxUint32 fileType, creator; + wxString extension = myData->extensions[i]; + + // Remove leading '*' + if (extension.length() && (extension.GetChar(0) == '*')) + extension = extension.Mid( 1 ); + + // Remove leading '.' + if (extension.length() && (extension.GetChar(0) == '.')) + extension = extension.Mid( 1 ); + + if (wxFileName::MacFindDefaultTypeAndCreator( extension, &fileType, &creator )) + myData->filtermactypes.Add( (OSType)fileType ); + else + myData->filtermactypes.Add( '****' ); // We'll fail safe if it's not recognized + } + } +} + +static Boolean CheckFile( const wxString &filename , OSType type , OpenUserDataRecPtr data) +{ + wxString file(filename) ; + file.MakeUpper() ; + + if ( data->extensions.GetCount() > 0 ) + { + //for ( int i = 0 ; i < data->numfilters ; ++i ) + int i = data->currentfilter ; + if ( data->extensions[i].Right(2) == wxT(".*") ) + return true ; + + { + if ( type == (OSType)data->filtermactypes[i] ) + return true ; + + wxStringTokenizer tokenizer( data->extensions[i] , wxT(";") ) ; + while ( tokenizer.HasMoreTokens() ) + { + wxString extension = tokenizer.GetNextToken() ; + if ( extension.GetChar(0) == '*' ) + extension = extension.Mid(1) ; + extension.MakeUpper(); + + if ( file.length() >= extension.length() && extension == file.Right(extension.length() ) ) + return true ; + } + } + + return false ; + } + + return true ; +} + +// end wxmac + +wxFileDialog::wxFileDialog( + wxWindow *parent, const wxString& message, + const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard, + long style, const wxPoint& pos, const wxSize& sz, const wxString& name) + : wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos, sz, name) +{ + wxASSERT_MSG( NavServicesAvailable() , wxT("Navigation Services are not running") ) ; +} + +pascal Boolean CrossPlatformFilterCallback( + AEDesc *theItem, + void *info, + void *callBackUD, + NavFilterModes filterMode ) +{ + OpenUserDataRecPtr data = (OpenUserDataRecPtr) callBackUD ; + + if (filterMode == kNavFilteringBrowserList) + { + // We allow navigation to all folders. For files, we check against the current + // filter string. + // However, packages should be dealt with like files and not like folders. So + // check if a folder is a package before deciding what to do. + NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*) info ; + FSRef fsref; + + if ( theInfo->isFolder ) + { + // check bundle bit (using Finder Services - used by OS9 on some bundles) + FSCatalogInfo catalogInfo; + if (FSGetCatalogInfo (&fsref, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL) != noErr) + return true; + + // Check bundle item (using Launch Services - used by OS-X through info.plist or APP) + LSItemInfoRecord lsInfo; + if (LSCopyItemInfoForRef(&fsref, kLSRequestBasicFlagsOnly, &lsInfo ) != noErr) + return true; + + // If it's not a bundle, then it's a normal folder and it passes our filter + FileInfo *fileInfo = (FileInfo *) catalogInfo.finderInfo; + if ( !(fileInfo->finderFlags & kHasBundle) && + !(lsInfo.flags & (kLSItemInfoIsApplication | kLSItemInfoIsPackage)) ) + return true; + } + else + { + AECoerceDesc (theItem, typeFSRef, theItem); + if ( AEGetDescData (theItem, &fsref, sizeof (FSRef)) == noErr) + { + wxString file = wxMacFSRefToPath( &fsref ) ; + return CheckFile( file , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ; + } + } + } + + return true; +} + +int wxFileDialog::ShowModal() +{ + OSErr err; + NavDialogCreationOptions dialogCreateOptions; + + // set default options + ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions); + + // this was always unset in the old code + dialogCreateOptions.optionFlags &= ~kNavSelectDefaultLocation; + + wxCFStringRef message(m_message, GetFont().GetEncoding()); + dialogCreateOptions.windowTitle = message; + + wxCFStringRef defaultFileName(m_fileName, GetFont().GetEncoding()); + dialogCreateOptions.saveFileName = defaultFileName; + + + NavDialogRef dialog; + NavObjectFilterUPP navFilterUPP = NULL; + OpenUserDataRec myData; + myData.defaultLocation = m_dir; + + MakeUserDataRec(&myData , m_wildCard); + myData.currentfilter = m_filterIndex; + size_t numFilters = myData.extensions.GetCount(); + if (numFilters) + { + CFMutableArrayRef popup = CFArrayCreateMutable( kCFAllocatorDefault , + numFilters , &kCFTypeArrayCallBacks ) ; + dialogCreateOptions.popupExtension = popup ; + myData.menuitems = dialogCreateOptions.popupExtension ; + for ( size_t i = 0 ; i < numFilters ; ++i ) + { + CFArrayAppendValue( popup , (CFStringRef) wxCFStringRef( myData.name[i] , GetFont().GetEncoding() ) ) ; + } + } + + if (HasFdFlag(wxFD_SAVE)) + { + myData.saveMode = true; + + dialogCreateOptions.optionFlags |= kNavDontAutoTranslate; + dialogCreateOptions.optionFlags |= kNavDontAddTranslateItems; + if (!numFilters) + dialogCreateOptions.optionFlags |= kNavNoTypePopup; + + // The extension is important + if (numFilters < 2) + dialogCreateOptions.optionFlags |= kNavPreserveSaveFileExtension; + + if (!(m_windowStyle & wxFD_OVERWRITE_PROMPT)) + dialogCreateOptions.optionFlags |= kNavDontConfirmReplacement; + + err = ::NavCreatePutFileDialog( + &dialogCreateOptions, + kNavGenericSignature, // Suppresses the 'Default' (top) menu item + kNavGenericSignature, + sStandardNavEventFilter, + &myData, // for defaultLocation + &dialog ); + } + else + { + // let the user select bundles/programs in dialogs + dialogCreateOptions.optionFlags |= kNavSupportPackages; + + navFilterUPP = NewNavObjectFilterUPP(CrossPlatformFilterCallback); + err = ::NavCreateGetFileDialog( + &dialogCreateOptions, + NULL, // NavTypeListHandle + sStandardNavEventFilter, + NULL, // NavPreviewUPP + navFilterUPP, + (void *) &myData, // inClientData + &dialog ); + } + + if (err == noErr) + err = ::NavDialogRun(dialog); + + // clean up filter related data, etc. + if (navFilterUPP) + ::DisposeNavObjectFilterUPP(navFilterUPP); + + if (err != noErr) + { + ::NavDialogDispose(dialog); + return wxID_CANCEL; + } + + NavReplyRecord navReply; + err = ::NavDialogGetReply(dialog, &navReply); + if (err == noErr && navReply.validRecord) + { + AEKeyword theKeyword; + DescType actualType; + Size actualSize; + FSRef theFSRef; + wxString thePath ; + long count; + + m_filterIndex = myData.currentfilter; + ::AECountItems( &navReply.selection, &count ); + for (long i = 1; i <= count; ++i) + { + err = ::AEGetNthPtr( + &(navReply.selection), i, typeFSRef, &theKeyword, &actualType, + &theFSRef, sizeof(theFSRef), &actualSize ); + if (err != noErr) + break; + + if (HasFdFlag(wxFD_SAVE)) + thePath = wxMacFSRefToPath( &theFSRef, navReply.saveFileName ); + else + thePath = wxMacFSRefToPath( &theFSRef ); + + if (!thePath) + { + ::NavDisposeReply(&navReply); + ::NavDialogDispose(dialog); + return wxID_CANCEL; + } + + m_path = thePath; + m_paths.Add(m_path); + m_fileName = wxFileNameFromPath(m_path); + m_fileNames.Add(m_fileName); + } + + // set these to the first hit + m_path = m_paths[0]; + m_fileName = wxFileNameFromPath(m_path); + m_dir = wxPathOnly(m_path); + } + + ::NavDisposeReply(&navReply); + ::NavDialogDispose(dialog); + + return (err == noErr) ? wxID_OK : wxID_CANCEL; +} + +#endif // wxUSE_FILEDLG + diff --git a/src/osx/carbon/font.cpp b/src/osx/carbon/font.cpp new file mode 100644 index 0000000000..d069f3c89e --- /dev/null +++ b/src/osx/carbon/font.cpp @@ -0,0 +1,1021 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/font.cpp +// Purpose: wxFont class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/font.h" + +#ifndef WX_PRECOMP + #include "wx/string.h" + #include "wx/utils.h" + #include "wx/intl.h" + #include "wx/gdicmn.h" + #include "wx/log.h" +#endif + +#include "wx/fontutil.h" +#include "wx/graphics.h" +#include "wx/settings.h" + +#include "wx/mac/uma.h" + +#ifndef __DARWIN__ +#include +#endif + +#include +#include + +IMPLEMENT_DYNAMIC_CLASS(wxFont, wxGDIObject) + + +class WXDLLEXPORT wxFontRefData: public wxGDIRefData +{ +public: + wxFontRefData() + { + Init(10, wxDEFAULT, wxNORMAL, wxNORMAL, + false, wxT("applicationfont"), wxFONTENCODING_DEFAULT); + } + + wxFontRefData(const wxFontRefData& data) + { + Init(data.m_pointSize, data.m_family, data.m_style, data.m_weight, + data.m_underlined, data.m_faceName, data.m_encoding); + } + + wxFontRefData(int size, + int family, + int style, + int weight, + bool underlined, + const wxString& faceName, + wxFontEncoding encoding) + { + Init(size, family, style, weight, underlined, faceName, encoding); + } + +#if wxMAC_USE_CORE_TEXT + wxFontRefData( wxUint32 coreTextFontType ); + wxFontRefData( CTFontRef font ); + wxFontRefData( CTFontDescriptorRef fontdescriptor, int size ); +#endif + + virtual ~wxFontRefData(); + + void SetNoAntiAliasing( bool no = true ) { m_noAA = no; } + + bool GetNoAntiAliasing() const { return m_noAA; } + + void SetPointSize( int size ) + { + m_pointSize = size; + MacInvalidateNativeFont(); + } + + int GetPointSize() const { return m_pointSize; } + + void SetFamily( int family ) + { + m_family = family; + MacInvalidateNativeFont(); + } + + + int GetFamily() const { return m_family; } + + void SetStyle( int style ) + { + m_style = style; + MacInvalidateNativeFont(); + } + + + int GetStyle() const { return m_style; } + + void SetWeight( int weight ) + { + m_weight = weight; + MacInvalidateNativeFont(); + } + + + int GetWeight() const { return m_weight; } + + void SetUnderlined( bool u ) + { + m_underlined = u; + MacInvalidateNativeFont(); + } + + bool GetUnderlined() const { return m_underlined; } + + void SetFaceName( const wxString& facename ) + { + m_faceName = facename; + MacInvalidateNativeFont(); + } + + const wxString& GetFaceName() const { return m_faceName; } + + void SetEncoding( wxFontEncoding encoding ) + { + m_encoding = encoding; + MacInvalidateNativeFont(); + } + + wxFontEncoding GetEncoding() const { return m_encoding; } + + void MacInvalidateNativeFont(); + + void MacFindFont(); + +protected: + // common part of all ctors + void Init(int size, + int family, + int style, + int weight, + bool underlined, + const wxString& faceName, + wxFontEncoding encoding); + +#if wxMAC_USE_CORE_TEXT + void Init( CTFontRef font ); +#endif + // font characterstics + int m_pointSize; + int m_family; + int m_style; + int m_weight; + bool m_underlined; + wxString m_faceName; + wxFontEncoding m_encoding; + bool m_noAA; // No anti-aliasing + +public: +#if wxMAC_USE_ATSU_TEXT + FMFontFamily m_macFontFamily; + FMFontSize m_macFontSize; + FMFontStyle m_macFontStyle; + + // ATSU Font Information + + // this is split into an ATSU font id that may + // contain some styles (special bold fonts etc) and + // these are the additional qd styles that are not + // included in the ATSU font id + ATSUFontID m_macATSUFontID; + FMFontStyle m_macATSUAdditionalQDStyles ; + + // for true themeing support we must store the correct font + // information here, as this speeds up and optimizes rendering + ThemeFontID m_macThemeFontID ; +#endif +#if wxMAC_USE_CORE_TEXT + wxCFRef m_ctFont; + wxCFRef m_ctFontDescriptor; +#endif +#if wxMAC_USE_CORE_TEXT || wxMAC_USE_ATSU_TEXT + ATSUStyle m_macATSUStyle ; +#endif + wxNativeFontInfo m_info; +}; + +#define M_FONTDATA ((wxFontRefData*)m_refData) + + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxFontRefData +// ---------------------------------------------------------------------------- + +void wxFontRefData::Init(int pointSize, + int family, + int style, + int weight, + bool underlined, + const wxString& faceName, + wxFontEncoding encoding) +{ + m_style = style; + m_pointSize = (pointSize == -1) ? wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).GetPointSize() : pointSize; + m_family = family; + m_style = style; + m_weight = weight; + m_underlined = underlined; + m_faceName = faceName; + m_encoding = encoding; + m_noAA = false; +#if wxMAC_USE_ATSU_TEXT + m_macFontFamily = 0 ; + m_macFontSize = 0; + m_macFontStyle = 0; + m_macATSUFontID = 0; + m_macATSUAdditionalQDStyles = 0 ; + m_macThemeFontID = kThemeCurrentPortFont ; +#endif +#if wxMAC_USE_CORE_TEXT || wxMAC_USE_ATSU_TEXT + m_macATSUStyle = NULL ; +#endif +} + +wxFontRefData::~wxFontRefData() +{ +#if wxMAC_USE_CORE_TEXT || wxMAC_USE_ATSU_TEXT + if ( m_macATSUStyle ) + { + ::ATSUDisposeStyle((ATSUStyle)m_macATSUStyle); + m_macATSUStyle = NULL ; + } +#endif +} + +void wxFontRefData::MacInvalidateNativeFont() +{ +#if wxMAC_USE_CORE_TEXT + m_ctFont.reset(); + m_ctFontDescriptor.reset(); +#endif +#if wxMAC_USE_CORE_TEXT || wxMAC_USE_ATSU_TEXT + if ( m_macATSUStyle ) + { + ::ATSUDisposeStyle((ATSUStyle)m_macATSUStyle); + m_macATSUStyle = NULL ; + } +#endif +} + +#if wxMAC_USE_CORE_TEXT + +/* from Core Text Manual Common Operations */ + +static CTFontDescriptorRef wxMacCreateCTFontDescriptor(CFStringRef iFamilyName, CTFontSymbolicTraits iTraits ) +{ + CTFontDescriptorRef descriptor = NULL; + CFMutableDictionaryRef attributes; + + assert(iFamilyName != NULL); + // Create a mutable dictionary to hold our attributes. + attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + check(attributes != NULL); + + if (attributes != NULL) { + // Add a family name to our attributes. + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, iFamilyName); + + + if ( iTraits ) { + CFMutableDictionaryRef traits; + CFNumberRef symTraits; + + // Create the traits dictionary. + symTraits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, + &iTraits); + check(symTraits != NULL); + + if (symTraits != NULL) { + // Create a dictionary to hold our traits values. + traits = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + check(traits != NULL); + + if (traits != NULL) { + // Add the symbolic traits value to the traits dictionary. + CFDictionaryAddValue(traits, kCTFontSymbolicTrait, symTraits); + + // Add the traits attribute to our attributes. + CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits); + CFRelease(traits); + } + CFRelease(symTraits); + } + } + // Create the font descriptor with our attributes + descriptor = CTFontDescriptorCreateWithAttributes(attributes); + check(descriptor != NULL); + + CFRelease(attributes); + } + // Return our font descriptor. + return descriptor ; +} + +wxFontRefData::wxFontRefData( wxUint32 coreTextFontType ) +{ + CTFontRef font = CTFontCreateUIFontForLanguage( coreTextFontType, 0.0, NULL ) ; + if ( CTFontGetSize(m_ctFont) == 0 ) + { + CFRelease(font); + font = CTFontCreateUIFontForLanguage( coreTextFontType, 12.0, NULL ); + } + Init( font ); +} + +wxFontRefData::wxFontRefData( CTFontRef font ) +{ + Init( font ); +} + +wxFontRefData::wxFontRefData( CTFontDescriptorRef fontdescriptor, int size ) +{ + if ( size == 0 ) + { + wxCFRef< CFNumberRef > value( (CFNumberRef) CTFontDescriptorCopyAttribute( fontdescriptor, kCTFontSizeAttribute ) ); + + float fsize; + if ( CFNumberGetValue( value , kCFNumberFloatType , &fsize ) ) + { + size = (int) fsize + 0.5 ; + } + } + Init( CTFontCreateWithFontDescriptor(fontdescriptor, size,NULL) ); +} + +void wxFontRefData::Init( CTFontRef font ) +{ + Init(10, wxDEFAULT, wxNORMAL, wxNORMAL, + false, wxT("applicationfont"), wxFONTENCODING_DEFAULT); + + m_ctFont.reset( font ); +} + +#endif + +void wxFontRefData::MacFindFont() +{ + +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + if ( m_faceName.empty() && m_family == wxDEFAULT ) + { + m_ctFont.reset(CTFontCreateUIFontForLanguage( kCTFontSystemFontType, 0.0, NULL )); + } + + if ( m_ctFont ) + { + wxCFStringRef name( CTFontCopyFamilyName( m_ctFont ) ); + m_faceName = name.AsString(); + m_pointSize = CTFontGetSize(m_ctFont) ; + CTFontSymbolicTraits traits = CTFontGetSymbolicTraits( m_ctFont ); + if ( traits & kCTFontItalicTrait ) + m_style = wxITALIC; + if ( traits & kCTFontBoldTrait ) + m_weight = wxBOLD ; + if ( !m_ctFontDescriptor.get() ) + m_ctFontDescriptor.reset( CTFontCopyFontDescriptor( m_ctFont ) ); + } + else + { + if ( m_faceName.empty() ) + { + switch ( m_family ) + { + case wxSCRIPT : + case wxROMAN : + case wxDECORATIVE : + m_faceName = wxT("Times"); + break ; + + case wxSWISS : + m_faceName = wxT("Lucida Grande"); + break ; + + case wxMODERN : + case wxTELETYPE: + m_faceName = wxT("Monaco"); + break ; + + default: + m_faceName = wxT("Times"); + break ; + } + } + + + CTFontSymbolicTraits traits = 0; + + if (m_weight == wxBOLD) + traits |= kCTFontBoldTrait; + if (m_style == wxITALIC || m_style == wxSLANT) + traits |= kCTFontItalicTrait; + +// use font descriptor caching +#if 1 + wxString lookupname = wxString::Format( "%s_%ld", m_faceName.c_str(), traits ); + + static std::map< std::wstring , wxCFRef< CTFontDescriptorRef > > fontdescriptorcache ; + + m_ctFontDescriptor = fontdescriptorcache[ std::wstring(lookupname.wc_str()) ]; + if ( !m_ctFontDescriptor ) + { + wxCFStringRef cf( m_faceName, wxLocale::GetSystemEncoding() ); + m_ctFontDescriptor.reset( wxMacCreateCTFontDescriptor( cf, traits ) ); + fontdescriptorcache[ std::wstring(lookupname.wc_str()) ] = m_ctFontDescriptor; + } +#else + wxCFStringRef cf( m_faceName, wxLocale::GetSystemEncoding() ); + m_ctFontDescriptor.reset( wxMacCreateCTFontDescriptor( cf, traits ) ); +#endif + +// use font caching +#if 1 + wxString lookupnameWithSize = wxString::Format( "%s_%ld_%ld", m_faceName.c_str(), traits, m_pointSize ); + + static std::map< std::wstring , wxCFRef< CTFontRef > > fontcache ; + m_ctFont = fontcache[ std::wstring(lookupnameWithSize.wc_str()) ]; + if ( !m_ctFont ) + { + m_ctFont.reset( CTFontCreateWithFontDescriptor( m_ctFontDescriptor, m_pointSize, NULL ) ); + fontcache[ std::wstring(lookupnameWithSize.wc_str()) ] = m_ctFont; + } +#else + m_ctFont.reset( CTFontCreateWithFontDescriptor( m_ctFontDescriptor, m_pointSize, NULL ) ); +#endif + } +#if wxMAC_USE_ATSU_TEXT == 0 + OSStatus status = noErr; + CTFontDescriptorRef desc = m_ctFontDescriptor ; + ATSFontRef atsfont = CTFontGetPlatformFont( m_ctFont, &desc ); + FMFont fmfont = FMGetFontFromATSFontRef( atsfont ); + ATSUAttributeTag atsuTags[] = + { + kATSUFontTag , + kATSUSizeTag , + kATSUVerticalCharacterTag, + kATSUQDBoldfaceTag , + kATSUQDItalicTag , + kATSUQDUnderlineTag , + }; + ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + sizeof( ATSUFontID ) , + sizeof( Fixed ) , + sizeof( ATSUVerticalCharacterType), + sizeof( Boolean ) , + sizeof( Boolean ) , + sizeof( Boolean ) , + }; + Boolean kTrue = true ; + Boolean kFalse = false ; + + Fixed atsuSize = IntToFixed( m_pointSize ); + ATSUVerticalCharacterType kHorizontal = kATSUStronglyHorizontal; + ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + &fmfont , + &atsuSize , + &kHorizontal, + (m_weight == wxBOLD) ? &kTrue : &kFalse , + (m_style == wxITALIC || m_style == wxSLANT) ? &kTrue : &kFalse , + (m_underlined) ? &kTrue : &kFalse , + }; + + if ( m_macATSUStyle ) + { + ::ATSUDisposeStyle((ATSUStyle)m_macATSUStyle); + m_macATSUStyle = NULL ; + } + status = ::ATSUCreateStyle((ATSUStyle *)&m_macATSUStyle); + wxASSERT_MSG( status == noErr , wxT("couldn't create ATSU style") ); + status = ::ATSUSetAttributes( + (ATSUStyle)m_macATSUStyle, + sizeof(atsuTags) / sizeof(ATSUAttributeTag) , + atsuTags, atsuSizes, atsuValues); +#endif + } +#endif +#if wxMAC_USE_ATSU_TEXT + { + OSStatus status = noErr; + Str255 qdFontName ; + if ( m_macThemeFontID != kThemeCurrentPortFont ) + { + Style style ; + GetThemeFont( m_macThemeFontID, GetApplicationScript(), qdFontName, &m_macFontSize, &style ); + if ( m_macFontSize == 0 ) + m_macFontSize = 12; + m_macFontStyle = style ; + m_faceName = wxMacMakeStringFromPascal( qdFontName ); + if ( m_macFontStyle & bold ) + m_weight = wxBOLD ; + else + m_weight = wxNORMAL ; + if ( m_macFontStyle & italic ) + m_style = wxITALIC ; + if ( m_macFontStyle & underline ) + m_underlined = true ; + m_pointSize = m_macFontSize ; + m_macFontFamily = FMGetFontFamilyFromName( qdFontName ); + } + else + { + if ( m_faceName.empty() ) + { + if ( m_family == wxDEFAULT ) + { + m_macFontFamily = GetAppFont(); + FMGetFontFamilyName(m_macFontFamily,qdFontName); + m_faceName = wxMacMakeStringFromPascal( qdFontName ); + } + else + { + switch ( m_family ) + { + case wxSCRIPT : + case wxROMAN : + case wxDECORATIVE : + m_faceName = wxT("Times"); + break ; + + case wxSWISS : + m_faceName = wxT("Lucida Grande"); + break ; + + case wxMODERN : + case wxTELETYPE: + m_faceName = wxT("Monaco"); + break ; + + default: + m_faceName = wxT("Times"); + break ; + } + wxMacStringToPascal( m_faceName , qdFontName ); + m_macFontFamily = FMGetFontFamilyFromName( qdFontName ); + if ( m_macFontFamily == kInvalidFontFamily ) + { + wxLogDebug( wxT("ATSFontFamilyFindFromName failed for %s"), m_faceName.c_str() ); + m_macFontFamily = GetAppFont(); + } + } + } + else + { + if ( m_faceName == wxT("systemfont") ) + m_macFontFamily = GetSysFont(); + else if ( m_faceName == wxT("applicationfont") ) + m_macFontFamily = GetAppFont(); + else + { + wxCFStringRef cf( m_faceName, wxLocale::GetSystemEncoding() ); + ATSFontFamilyRef atsfamily = ATSFontFamilyFindFromName( cf , kATSOptionFlagsDefault ); + if ( atsfamily == (ATSFontFamilyRef) -1 ) + { + wxLogDebug( wxT("ATSFontFamilyFindFromName failed for ") + m_faceName ); + m_macFontFamily = GetAppFont(); + } + else + m_macFontFamily = FMGetFontFamilyFromATSFontFamilyRef( atsfamily ); + } + } + + m_macFontStyle = 0; + if (m_weight == wxBOLD) + m_macFontStyle |= bold; + if (m_style == wxITALIC || m_style == wxSLANT) + m_macFontStyle |= italic; + if (m_underlined) + m_macFontStyle |= underline; + m_macFontSize = m_pointSize ; + } + + // we try to get as much styles as possible into ATSU + + + // ATSUFontID and FMFont are equivalent + FMFontStyle intrinsicStyle = 0 ; + status = FMGetFontFromFontFamilyInstance( m_macFontFamily , m_macFontStyle , &m_macATSUFontID , &intrinsicStyle); + wxASSERT_MSG( status == noErr , wxT("couldn't get an ATSUFont from font family") ); + m_macATSUAdditionalQDStyles = m_macFontStyle & (~intrinsicStyle ); + + if ( m_macATSUStyle ) + { + ::ATSUDisposeStyle((ATSUStyle)m_macATSUStyle); + m_macATSUStyle = NULL ; + } + + status = ::ATSUCreateStyle((ATSUStyle *)&m_macATSUStyle); + wxASSERT_MSG( status == noErr , wxT("couldn't create ATSU style") ); + + ATSUAttributeTag atsuTags[] = + { + kATSUFontTag , + kATSUSizeTag , + kATSUVerticalCharacterTag, + kATSUQDBoldfaceTag , + kATSUQDItalicTag , + kATSUQDUnderlineTag , + kATSUQDCondensedTag , + kATSUQDExtendedTag , + }; + ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + sizeof( ATSUFontID ) , + sizeof( Fixed ) , + sizeof( ATSUVerticalCharacterType), + sizeof( Boolean ) , + sizeof( Boolean ) , + sizeof( Boolean ) , + sizeof( Boolean ) , + sizeof( Boolean ) , + }; + + Boolean kTrue = true ; + Boolean kFalse = false ; + + Fixed atsuSize = IntToFixed( m_macFontSize ); + ATSUVerticalCharacterType kHorizontal = kATSUStronglyHorizontal; + ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + &m_macATSUFontID , + &atsuSize , + &kHorizontal, + (m_macATSUAdditionalQDStyles & bold) ? &kTrue : &kFalse , + (m_macATSUAdditionalQDStyles & italic) ? &kTrue : &kFalse , + (m_macATSUAdditionalQDStyles & underline) ? &kTrue : &kFalse , + (m_macATSUAdditionalQDStyles & condense) ? &kTrue : &kFalse , + (m_macATSUAdditionalQDStyles & extend) ? &kTrue : &kFalse , + }; + + status = ::ATSUSetAttributes( + (ATSUStyle)m_macATSUStyle, + sizeof(atsuTags) / sizeof(ATSUAttributeTag) , + atsuTags, atsuSizes, atsuValues); + + wxASSERT_MSG( status == noErr , wxT("couldn't modify ATSU style") ); + return; + } +#endif +} + +// ---------------------------------------------------------------------------- +// wxFont +// ---------------------------------------------------------------------------- + +bool wxFont::Create(const wxNativeFontInfo& info) +{ + return Create( + info.pointSize, info.family, info.style, info.weight, + info.underlined, info.faceName, info.encoding ); +} + +wxFont::wxFont(const wxString& fontdesc) +{ + wxNativeFontInfo info; + if ( info.FromString(fontdesc) ) + (void)Create(info); +} + +bool wxFont::Create(int pointSize, + int family, + int style, + int weight, + bool underlined, + const wxString& faceName, + wxFontEncoding encoding) +{ + UnRef(); + + m_refData = new wxFontRefData( + pointSize, family, style, weight, + underlined, faceName, encoding); + + RealizeResource(); + + return true; +} + +#if wxMAC_USE_CORE_TEXT + +bool wxFont::MacCreateFromUIFont(wxUint32 ctFontType ) +{ + UnRef(); + + m_refData = new wxFontRefData(ctFontType); + RealizeResource(); + + return true; +} + +bool wxFont::MacCreateFromCTFontDescriptor( const void * ctFontDescriptor , int size ) +{ + UnRef(); + + m_refData = new wxFontRefData((CTFontDescriptorRef)ctFontDescriptor, size);; + RealizeResource(); + + return true; +} + + +#endif + +bool wxFont::MacCreateFromThemeFont(wxUint16 themeFontID) +{ +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050) + { + return MacCreateFromUIFont(HIThemeGetUIFontType(themeFontID)); + } +#endif +#if wxMAC_USE_ATSU_TEXT + { + UnRef(); + + m_refData = new wxFontRefData( + 12, wxDEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, + false, wxEmptyString, wxFONTENCODING_DEFAULT ); + + M_FONTDATA->m_macThemeFontID = themeFontID ; + RealizeResource(); + return true; + } +#endif + return false; +} + +wxFont::~wxFont() +{ +} + +bool wxFont::RealizeResource() +{ + M_FONTDATA->MacFindFont(); + + return true; +} + +void wxFont::SetEncoding(wxFontEncoding encoding) +{ + Unshare(); + + M_FONTDATA->SetEncoding( encoding ); + + RealizeResource(); +} + +void wxFont::Unshare() +{ + // Don't change shared data + if (!m_refData) + { + m_refData = new wxFontRefData(); + } + else + { + wxFontRefData* ref = new wxFontRefData(*(wxFontRefData*)m_refData); + UnRef(); + m_refData = ref; + } +} + +wxGDIRefData *wxFont::CreateGDIRefData() const +{ + return new wxFontRefData; +} + +wxGDIRefData *wxFont::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxFontRefData(*wx_static_cast(const wxFontRefData *, data)); +} + +void wxFont::SetPointSize(int pointSize) +{ + if ( M_FONTDATA->GetPointSize() == pointSize ) + return; + + Unshare(); + + M_FONTDATA->SetPointSize( pointSize ); + + RealizeResource(); +} + +void wxFont::SetFamily(int family) +{ + Unshare(); + + M_FONTDATA->SetFamily( family ); + + RealizeResource(); +} + +void wxFont::SetStyle(int style) +{ + Unshare(); + + M_FONTDATA->SetStyle( style ); + + RealizeResource(); +} + +void wxFont::SetWeight(int weight) +{ + Unshare(); + + M_FONTDATA->SetWeight( weight ); + + RealizeResource(); +} + +bool wxFont::SetFaceName(const wxString& faceName) +{ + Unshare(); + + M_FONTDATA->SetFaceName( faceName ); + + RealizeResource(); + + return wxFontBase::SetFaceName(faceName); +} + +void wxFont::SetUnderlined(bool underlined) +{ + Unshare(); + + M_FONTDATA->SetUnderlined( underlined ); + + RealizeResource(); +} + +void wxFont::SetNoAntiAliasing( bool no ) +{ + Unshare(); + + M_FONTDATA->SetNoAntiAliasing( no ); + + RealizeResource(); +} + +// ---------------------------------------------------------------------------- +// accessors +// ---------------------------------------------------------------------------- + +// TODO: insert checks everywhere for M_FONTDATA == NULL! + +int wxFont::GetPointSize() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->GetPointSize(); +} + +wxSize wxFont::GetPixelSize() const +{ +#if wxUSE_GRAPHICS_CONTEXT + // TODO: consider caching the value + wxGraphicsContext* dc = wxGraphicsContext::CreateFromNative((CGContextRef) NULL); + dc->SetFont(*(wxFont *)this,*wxBLACK); + wxDouble width, height = 0; + dc->GetTextExtent( wxT("g"), &width, &height, NULL, NULL); + delete dc; + return wxSize((int)width, (int)height); +#else + return wxFontBase::GetPixelSize(); +#endif +} + +int wxFont::GetFamily() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->GetFamily(); +} + +int wxFont::GetStyle() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->GetStyle() ; +} + +int wxFont::GetWeight() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->GetWeight(); +} + +bool wxFont::GetUnderlined() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , false, wxT("invalid font") ); + + return M_FONTDATA->GetUnderlined(); +} + +wxString wxFont::GetFaceName() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , wxEmptyString , wxT("invalid font") ); + + return M_FONTDATA->GetFaceName() ; +} + +wxFontEncoding wxFont::GetEncoding() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , wxFONTENCODING_DEFAULT , wxT("invalid font") ); + + return M_FONTDATA->GetEncoding() ; +} + +bool wxFont::GetNoAntiAliasing() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , false, wxT("invalid font") ); + + return M_FONTDATA->GetNoAntiAliasing(); +} + +#if wxMAC_USE_ATSU_TEXT + +short wxFont::MacGetFontNum() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->m_macFontFamily; +} + +short wxFont::MacGetFontSize() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->m_macFontSize; +} + +wxByte wxFont::MacGetFontStyle() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->m_macFontStyle; +} + +wxUint32 wxFont::MacGetATSUFontID() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->m_macATSUFontID; +} + +wxUint32 wxFont::MacGetATSUAdditionalQDStyles() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->m_macATSUAdditionalQDStyles; +} + +wxUint16 wxFont::MacGetThemeFontID() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return M_FONTDATA->m_macThemeFontID; +} +#endif + +#if wxMAC_USE_CORE_TEXT || wxMAC_USE_ATSU_TEXT +void * wxFont::MacGetATSUStyle() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , NULL, wxT("invalid font") ); + + return M_FONTDATA->m_macATSUStyle; +} +#endif + +#if wxMAC_USE_CORE_TEXT + +const void * wxFont::MacGetCTFont() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return (CTFontRef)(M_FONTDATA->m_ctFont); +} + +const void * wxFont::MacGetCTFontDescriptor() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , 0, wxT("invalid font") ); + + return (CTFontDescriptorRef)(M_FONTDATA->m_ctFontDescriptor); +} + +#endif + +const wxNativeFontInfo * wxFont::GetNativeFontInfo() const +{ + wxCHECK_MSG( M_FONTDATA != NULL , NULL, wxT("invalid font") ); + wxCHECK_MSG( Ok(), NULL, wxT("invalid font") ); + + M_FONTDATA->m_info.InitFromFont(*this); + + return &(M_FONTDATA->m_info); +} diff --git a/src/osx/carbon/fontdlg.cpp b/src/osx/carbon/fontdlg.cpp new file mode 100644 index 0000000000..e7892ad542 --- /dev/null +++ b/src/osx/carbon/fontdlg.cpp @@ -0,0 +1,828 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/fontdlg.cpp +// Purpose: wxFontDialog class for carbon 10.2+. +// Author: Ryan Norton +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// =========================================================================== +// declarations +// =========================================================================== + +// --------------------------------------------------------------------------- +// headers +// --------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_FONTDLG + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/crt.h" + #include "wx/dcclient.h" + #include "wx/frame.h" + #include "wx/textctrl.h" + #include "wx/listbox.h" + #include "wx/checkbox.h" + #include "wx/choice.h" + #include "wx/sizer.h" + #include "wx/stattext.h" + #include "wx/button.h" +#endif + +#include "wx/fontdlg.h" + +#if wxMAC_USE_EXPERIMENTAL_FONTDIALOG + +IMPLEMENT_DYNAMIC_CLASS(wxFontDialog, wxDialog) + +#include "wx/mac/uma.h" + +// --------------------------------------------------------------------------- +// wxFontDialog +// --------------------------------------------------------------------------- + +static const EventTypeSpec eventList[] = +{ + { kEventClassFont, kEventFontSelection } , +} ; + + +pascal OSStatus +wxMacCarbonFontPanelHandler(EventHandlerCallRef WXUNUSED(nextHandler), + EventRef event, + void *userData) +{ + OSStatus result = eventNotHandledErr ; + wxFontDialog *fontdialog = (wxFontDialog*) userData ; + wxFontData& fontdata= fontdialog->GetFontData() ; + + wxMacCarbonEvent cEvent( event ); + switch(cEvent.GetKind()) + { + case kEventFontSelection : + { + bool setup = false ; +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + CTFontDescriptorRef descr; + if ( cEvent.GetParameter( kEventParamCTFontDescriptor, typeCTFontDescriptorRef, &descr ) == noErr ) + { + wxFont font; + font.MacCreateFromCTFontDescriptor(descr); + fontdata.SetChosenFont( font ) ; + setup = true; + } + } +#endif +#if wxMAC_USE_ATSU_TEXT + ATSUFontID fontId = 0 ; + if ( !setup && (cEvent.GetParameter(kEventParamATSUFontID, &fontId) == noErr) ) + { + FMFontStyle fontStyle = cEvent.GetParameter(kEventParamFMFontStyle); + FMFontSize fontSize = cEvent.GetParameter(kEventParamFMFontSize); + + CFStringRef cfName = NULL; +#if 1 + FMFontFamily fontFamily = cEvent.GetParameter(kEventParamFMFontFamily); + ATSFontFamilyRef atsfontfamilyref = FMGetATSFontFamilyRefFromFontFamily( fontFamily ) ; + OSStatus err = ATSFontFamilyGetName( atsfontfamilyref , kATSOptionFlagsDefault , &cfName ) ; + if ( err == noErr ) + { + wxFAIL_MSG("ATSFontFamilyGetName failed"); + } +#else + // we don't use the ATSU naming anymore + ByteCount actualLength = 0; + char *c = NULL; + OSStatus err = ATSUFindFontName(fontId , kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode, + kFontNoLanguageCode , 0 , NULL , &actualLength , NULL ); + if ( err == noErr) + { + actualLength += 1 ; + char *c = (char*)malloc( actualLength ); + err = ATSUFindFontName(fontId, kFontFamilyName, kFontUnicodePlatform, kFontNoScriptCode, + kFontNoLanguageCode, actualLength, c , NULL, NULL); + cfName = CFStringCreateWithCharacters(NULL, (UniChar*) c, (actualLength-1) >> 1); + } + else + { + err = ATSUFindFontName(fontId , kFontFamilyName, kFontNoPlatformCode, kFontNoScriptCode, + kFontNoLanguageCode , 0 , NULL , &actualLength , NULL ); + if ( err == noErr ) + { + actualLength += 1 ; + c = (char*)malloc(actualLength); + err = ATSUFindFontName(fontId, kFontFamilyName, kFontNoPlatformCode, kFontNoScriptCode, + kFontNoLanguageCode, actualLength, c , NULL, NULL); + c[actualLength-1] = 0; + cfName = CFStringCreateWithCString(NULL, c, kCFStringEncodingMacRoman ); + } + } + if ( c!=NULL ) + free(c); +#endif + if ( cfName!=NULL ) + { + fontdata.m_chosenFont.SetFaceName(wxCFStringRef(cfName).AsString(wxLocale::GetSystemEncoding())); + fontdata.m_chosenFont.SetPointSize(fontSize); + fontdata.m_chosenFont.SetStyle(fontStyle & italic ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL); + fontdata.m_chosenFont.SetUnderlined((fontStyle & underline)!=0); + fontdata.m_chosenFont.SetWeight(fontStyle & bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL); + } + } +#endif // wxMAC_USE_ATSU_TEXT + + // retrieving the color + RGBColor fontColor ; + if ( cEvent.GetParameter(kEventParamFontColor, &fontColor) == noErr ) + { + fontdata.m_fontColour = fontColor; + } + else + { + CFDictionaryRef dict ; + if ( cEvent.GetParameter(kEventParamDictionary, &dict) == noErr ) + { + CFDictionaryRef attributesDict ; + if ( CFDictionaryGetValueIfPresent(dict, kFontPanelAttributesKey, (const void **)&attributesDict) ) + { + CFDataRef tagsData; + CFDataRef sizesData; + CFDataRef valuesData; + if ( CFDictionaryGetValueIfPresent(attributesDict, kFontPanelAttributeTagsKey, (const void **)&tagsData) && + CFDictionaryGetValueIfPresent(attributesDict, kFontPanelAttributeSizesKey, (const void **)&sizesData) && + CFDictionaryGetValueIfPresent(attributesDict, kFontPanelAttributeValuesKey, (const void **)&valuesData) ) + { + ItemCount count = CFDataGetLength(tagsData)/sizeof(ATSUAttributeTag); + ATSUAttributeTag *tagPtr = (ATSUAttributeTag *)CFDataGetBytePtr(tagsData); + ByteCount *sizePtr = (ByteCount *)CFDataGetBytePtr(sizesData); + UInt32 *bytePtr = (UInt32*)CFDataGetBytePtr(valuesData); + ATSUAttributeValuePtr valuesPtr = bytePtr ; + for ( ItemCount i = 0 ; i < count ; ++i) + { + if ( tagPtr[i] == kATSUColorTag && sizePtr[i] == sizeof(RGBColor)) + { + fontdata.m_fontColour = *(RGBColor *)valuesPtr; + break ; + } + bytePtr = (UInt32*)( (UInt8*)bytePtr + sizePtr[i]); + } + } + } + } + } + } + break ; + } + + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacCarbonFontPanelHandler ) + +wxFontDialog::wxFontDialog() +{ +} + +wxFontDialog::wxFontDialog(wxWindow *parent, const wxFontData& data) +{ + Create(parent, data); +} + +wxFontDialog::~wxFontDialog() +{ +} + +bool wxFontDialog::Create(wxWindow *WXUNUSED(parent), const wxFontData& data) +{ + m_fontData = data; + return true ; +} + +int wxFontDialog::ShowModal() +{ + OSStatus err ; + wxFont font = *wxNORMAL_FONT ; + if ( m_fontData.m_initialFont.Ok() ) + { + font = m_fontData.m_initialFont ; + } + + bool setup = false; +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + CTFontDescriptorRef descr = (CTFontDescriptorRef)font.MacGetCTFontDescriptor(); + err = SetFontInfoForSelection (kFontSelectionCoreTextType,1, &descr , NULL); + setup = true; + } +#endif +#if wxMAC_USE_ATSU_TEXT + if ( !setup ) + { + ATSUStyle style = (ATSUStyle)font.MacGetATSUStyle(); + err = SetFontInfoForSelection (kFontSelectionATSUIType,1, &style , NULL); + setup = true; + } +#endif + // just clicking on ENTER will not send us any font setting event, therefore we have to make sure + // that field is already correct + m_fontData.m_chosenFont = font ; + + EventHandlerRef handler ; + + err = InstallApplicationEventHandler( GetwxMacCarbonFontPanelHandlerUPP(), GetEventTypeCount(eventList), eventList, this , &handler ); + + if ( !FPIsFontPanelVisible() ) + FPShowHideFontPanel(); + + int retval = RunMixedFontDialog(this); + + ::RemoveEventHandler(handler); + + return retval ; +} + +#else + +#if !USE_NATIVE_FONT_DIALOG_FOR_MACOSX + +#undef wxFontDialog + +#include "wx/mac/fontdlg.h" + +#include "wx/fontenum.h" +#include "wx/colordlg.h" +#include "wx/spinctrl.h" + +// --------------------------------------------------------------------------- +// wxFontDialog stub for mac OS's without a native font dialog +// --------------------------------------------------------------------------- + +static const wxChar *FontFamilyIntToString(int family); +static int FontFamilyStringToInt(const wxChar *family); + + +//----------------------------------------------------------------------------- +// helper class - wxFontPreviewCtrl +//----------------------------------------------------------------------------- + +class WXDLLEXPORT wxFontPreviewCtrl : public wxWindow +{ +public: + wxFontPreviewCtrl(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& sz = wxDefaultSize, long style = 0) : + wxWindow(parent, id, pos, sz, style) + { + SetBackgroundColour(*wxWHITE); + } + +private: + void OnPaint(wxPaintEvent& event); + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(wxFontPreviewCtrl, wxWindow) + EVT_PAINT(wxFontPreviewCtrl::OnPaint) +END_EVENT_TABLE() + +void wxFontPreviewCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) +{ + wxPaintDC dc(this); + + wxSize size = GetSize(); + wxFont font = GetFont(); + + if ( font.Ok() ) + { + dc.SetFont(font); + // Calculate vertical centre + long w = 0, h = 0; + dc.GetTextExtent( wxT("X"), &w, &h); + dc.SetTextForeground(GetForegroundColour()); + dc.SetClippingRegion(2, 2, size.x-4, size.y-4); + dc.DrawText(_("ABCDEFGabcdefg12345"), + 10, size.y/2 - h/2); + dc.DestroyClippingRegion(); + } +} + +/* + * A control for displaying a small preview of a colour or bitmap + */ + +class wxFontColourSwatchCtrl: public wxControl +{ + DECLARE_CLASS(wxFontColourSwatchCtrl) +public: + wxFontColourSwatchCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0); + virtual ~wxFontColourSwatchCtrl(); + + void OnPaint(wxPaintEvent& event); + void OnMouseEvent(wxMouseEvent& event); + + void SetColour(const wxColour& colour) { m_colour = colour; SetBackgroundColour(m_colour); } + + wxColour& GetColour() { return m_colour; } + + virtual wxSize DoGetBestSize() const { return GetSize(); } + +protected: + wxColour m_colour; + +DECLARE_EVENT_TABLE() +}; + +/* + * A control for displaying a small preview of a colour or bitmap + */ + +BEGIN_EVENT_TABLE(wxFontColourSwatchCtrl, wxControl) + EVT_MOUSE_EVENTS(wxFontColourSwatchCtrl::OnMouseEvent) +END_EVENT_TABLE() + +IMPLEMENT_CLASS(wxFontColourSwatchCtrl, wxControl) + +wxFontColourSwatchCtrl::wxFontColourSwatchCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style): + wxControl(parent, id, pos, size, style) +{ + SetColour(* wxWHITE); + SetBackgroundStyle(wxBG_STYLE_COLOUR); +} + +wxFontColourSwatchCtrl::~wxFontColourSwatchCtrl() +{ +} + +void wxFontColourSwatchCtrl::OnMouseEvent(wxMouseEvent& event) +{ + if (event.LeftDown()) + { + wxWindow* parent = GetParent(); + while (parent != NULL && !parent->IsKindOf(CLASSINFO(wxDialog)) && !parent->IsKindOf(CLASSINFO(wxFrame))) + parent = parent->GetParent(); + + wxColourData data; + data.SetChooseFull(true); + data.SetColour(m_colour); + wxColourDialog *dialog = new wxColourDialog(parent, &data); + // Crashes on wxMac (no m_peer) +#ifndef __WXMAC__ + dialog->SetTitle(_("Background colour")); +#endif + if (dialog->ShowModal() == wxID_OK) + { + wxColourData retData = dialog->GetColourData(); + m_colour = retData.GetColour(); + SetBackgroundColour(m_colour); + } + dialog->Destroy(); + Refresh(); + + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId()); + HandleWindowEvent(event); + } +} + +/*! + * wxFontDialog type definition + */ + +IMPLEMENT_DYNAMIC_CLASS( wxFontDialog, wxDialog ) + +/*! + * wxFontDialog event table definition + */ + +BEGIN_EVENT_TABLE( wxFontDialog, wxDialog ) + EVT_LISTBOX( wxID_FONTDIALOG_FACENAME, wxFontDialog::OnFontdialogFacenameSelected ) + EVT_SPINCTRL( wxID_FONTDIALOG_FONTSIZE, wxFontDialog::OnFontdialogFontsizeUpdated ) + EVT_TEXT( wxID_FONTDIALOG_FONTSIZE, wxFontDialog::OnFontdialogFontsizeTextUpdated ) + EVT_CHECKBOX( wxID_FONTDIALOG_BOLD, wxFontDialog::OnFontdialogBoldClick ) + EVT_CHECKBOX( wxID_FONTDIALOG_ITALIC, wxFontDialog::OnFontdialogItalicClick ) + EVT_CHECKBOX( wxID_FONTDIALOG_UNDERLINED, wxFontDialog::OnFontdialogUnderlinedClick ) + EVT_BUTTON( wxID_OK, wxFontDialog::OnOkClick ) + EVT_BUTTON(wxID_FONTDIALOG_COLOUR, wxFontDialog::OnColourChanged) +END_EVENT_TABLE() + +/*! + * wxFontDialog constructors + */ + +wxFontDialog::wxFontDialog( ) +{ + m_dialogParent = NULL; +} + +wxFontDialog::wxFontDialog(wxWindow* parent, const wxFontData& fontData) +{ + m_dialogParent = NULL; + + Create(parent, fontData); +} + +wxFontDialog::~wxFontDialog() +{ + // empty +} + +/*! + * wxFontDialog creator + */ + +bool wxFontDialog::Create(wxWindow* parent, const wxFontData& fontData) +{ + m_fontData = fontData; + m_suppressUpdates = false; + + wxString caption = _("Font"); + + m_facenameCtrl = NULL; + m_sizeCtrl = NULL; + m_boldCtrl = NULL; + m_italicCtrl = NULL; + m_underlinedCtrl = NULL; + m_colourCtrl = NULL; + m_previewCtrl = NULL; + + InitializeFont(); + + SetExtraStyle(GetExtraStyle()|wxWS_EX_BLOCK_EVENTS); + wxDialog::Create( parent, wxID_ANY, caption, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + + CreateControls(); + GetSizer()->Fit(this); + GetSizer()->SetSizeHints(this); + Centre(); + + return true; +} + +/*! + * Control creation for wxFontDialog + */ + +void wxFontDialog::CreateControls() +{ + wxFontDialog* itemDialog1 = this; + + wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL); + itemDialog1->SetSizer(itemBoxSizer2); + + wxBoxSizer* itemBoxSizer3 = new wxBoxSizer(wxVERTICAL); + itemBoxSizer2->Add(itemBoxSizer3, 1, wxGROW|wxALL, 5); + + wxFlexGridSizer* itemFlexGridSizer4 = new wxFlexGridSizer(6, 2, 10, 0); + itemFlexGridSizer4->AddGrowableRow(4); + itemFlexGridSizer4->AddGrowableCol(1); + itemBoxSizer3->Add(itemFlexGridSizer4, 1, wxGROW|wxALL, 5); + + wxStaticText* itemStaticText5 = new wxStaticText( itemDialog1, wxID_STATIC, _("Font:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemFlexGridSizer4->Add(itemStaticText5, 0, wxALIGN_RIGHT|wxALIGN_TOP|wxALL, 5); + + wxBoxSizer* itemBoxSizer6 = new wxBoxSizer(wxVERTICAL); + itemFlexGridSizer4->Add(itemBoxSizer6, 0, wxGROW|wxGROW, 5); + + wxString* m_facenameCtrlStrings = NULL; + m_facenameCtrl = new wxListBox( itemDialog1, wxID_FONTDIALOG_FACENAME, wxDefaultPosition, wxSize(320, 100), 0, m_facenameCtrlStrings, wxLB_SINGLE ); + itemBoxSizer6->Add(m_facenameCtrl, 0, wxGROW|wxALL, 5); + + wxStaticText* itemStaticText8 = new wxStaticText( itemDialog1, wxID_STATIC, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemFlexGridSizer4->Add(itemStaticText8, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5); + + m_sizeCtrl = new wxSpinCtrl( itemDialog1, wxID_FONTDIALOG_FONTSIZE, _T("12"), wxDefaultPosition, wxSize(60, -1), wxSP_ARROW_KEYS, 1, 300, 12 ); + m_sizeCtrl->SetHelpText(_("The font size in points.")); + if (ShowToolTips()) + m_sizeCtrl->SetToolTip(_("The font size in points.")); + itemFlexGridSizer4->Add(m_sizeCtrl, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5); + + wxStaticText* itemStaticText10 = new wxStaticText( itemDialog1, wxID_STATIC, _("Style:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemFlexGridSizer4->Add(itemStaticText10, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5); + + wxBoxSizer* itemBoxSizer11 = new wxBoxSizer(wxHORIZONTAL); + itemFlexGridSizer4->Add(itemBoxSizer11, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL, 5); + + m_boldCtrl = new wxCheckBox( itemDialog1, wxID_FONTDIALOG_BOLD, _("Bold"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE ); + m_boldCtrl->SetValue(false); + m_boldCtrl->SetHelpText(_("Check to make the font bold.")); + if (ShowToolTips()) + m_boldCtrl->SetToolTip(_("Check to make the font bold.")); + itemBoxSizer11->Add(m_boldCtrl, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + m_italicCtrl = new wxCheckBox( itemDialog1, wxID_FONTDIALOG_ITALIC, _("Italic"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE ); + m_italicCtrl->SetValue(false); + m_italicCtrl->SetHelpText(_("Check to make the font italic.")); + if (ShowToolTips()) + m_italicCtrl->SetToolTip(_("Check to make the font italic.")); + itemBoxSizer11->Add(m_italicCtrl, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + if (m_fontData.GetEnableEffects()) + { + m_underlinedCtrl = new wxCheckBox( itemDialog1, wxID_FONTDIALOG_UNDERLINED, _("Underlined"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE ); + m_underlinedCtrl->SetValue(false); + m_underlinedCtrl->SetHelpText(_("Check to make the font underlined.")); + if (ShowToolTips()) + m_underlinedCtrl->SetToolTip(_("Check to make the font underlined.")); + itemBoxSizer11->Add(m_underlinedCtrl, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + } + + if (m_fontData.GetEnableEffects()) + { + wxStaticText* itemStaticText15 = new wxStaticText( itemDialog1, wxID_STATIC, _("Colour:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemFlexGridSizer4->Add(itemStaticText15, 0, wxALIGN_RIGHT|wxALIGN_TOP|wxALL, 5); + + m_colourCtrl = new wxFontColourSwatchCtrl( itemDialog1, wxID_FONTDIALOG_COLOUR, wxDefaultPosition, wxSize(-1, 30), wxSUNKEN_BORDER|wxTAB_TRAVERSAL ); + m_colourCtrl->SetHelpText(_("Click to change the font colour.")); + if (ShowToolTips()) + m_colourCtrl->SetToolTip(_("Click to change the font colour.")); + itemFlexGridSizer4->Add(m_colourCtrl, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5); + } + + wxStaticText* itemStaticText17 = new wxStaticText( itemDialog1, wxID_STATIC, _("Preview:"), wxDefaultPosition, wxDefaultSize, 0 ); + itemFlexGridSizer4->Add(itemStaticText17, 0, wxALIGN_RIGHT|wxALIGN_TOP|wxALL, 5); + + m_previewCtrl = new wxFontPreviewCtrl( itemDialog1, wxID_FONTDIALOG_PREVIEW, wxDefaultPosition, wxSize(-1, 70), wxSUNKEN_BORDER|wxTAB_TRAVERSAL ); + m_previewCtrl->SetHelpText(_("Shows a preview of the font.")); + if (ShowToolTips()) + m_previewCtrl->SetToolTip(_("Shows a preview of the font.")); + itemFlexGridSizer4->Add(m_previewCtrl, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5); + + wxBoxSizer* itemBoxSizer19 = new wxBoxSizer(wxHORIZONTAL); + itemBoxSizer3->Add(itemBoxSizer19, 0, wxALIGN_RIGHT|wxALL, 5); + + wxButton* itemButton20 = new wxButton( itemDialog1, wxID_CANCEL, _("&Cancel"), wxDefaultPosition, wxDefaultSize, 0 ); + itemButton20->SetHelpText(_("Click to cancel changes to the font.")); + if (ShowToolTips()) + itemButton20->SetToolTip(_("Click to cancel changes to the font.")); + itemBoxSizer19->Add(itemButton20, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + wxButton* itemButton21 = new wxButton( itemDialog1, wxID_OK, _("&OK"), wxDefaultPosition, wxDefaultSize, 0 ); + itemButton21->SetDefault(); + itemButton21->SetHelpText(_("Click to confirm changes to the font.")); + if (ShowToolTips()) + itemButton21->SetToolTip(_("Click to confirm changes to the font.")); + itemBoxSizer19->Add(itemButton21, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + + wxFontEnumerator enumerator; + enumerator.EnumerateFacenames(); + wxArrayString facenames = enumerator.GetFacenames(); + if (facenames) + { + facenames.Add(_("")); + facenames.Add(_("")); + facenames.Add(_("")); + facenames.Add(_("")); + facenames.Add(_("")); + facenames.Add(_("")); + facenames.Add(_("")); + facenames.Sort(); + m_facenameCtrl->Append(facenames); + } + + InitializeControls(); + m_previewCtrl->SetFont(m_dialogFont); + if (m_fontData.GetColour().Ok()) + { + m_previewCtrl->SetForegroundColour(m_fontData.GetColour()); + } + m_previewCtrl->Refresh(); +} + +/*! + * wxEVT_COMMAND_SPINCTRL_UPDATED event handler for wxID_FONTDIALOG_FONTSIZE + */ + +void wxFontDialog::OnFontdialogFontsizeUpdated( wxSpinEvent& WXUNUSED(event) ) +{ + ChangeFont(); +} + +/*! + * wxEVT_COMMAND_TEXT_UPDATED event handler for wxID_FONTDIALOG_FONTSIZE + */ + +void wxFontDialog::OnFontdialogFontsizeTextUpdated( wxCommandEvent& WXUNUSED(event) ) +{ + ChangeFont(); +} + +/*! + * wxEVT_COMMAND_CHECKBOX_CLICKED event handler for wxID_FONTDIALOG_BOLD + */ + +void wxFontDialog::OnFontdialogBoldClick( wxCommandEvent& WXUNUSED(event) ) +{ + ChangeFont(); +} + +/*! + * wxEVT_COMMAND_CHECKBOX_CLICKED event handler for wxID_FONTDIALOG_ITALIC + */ + +void wxFontDialog::OnFontdialogItalicClick( wxCommandEvent& WXUNUSED(event) ) +{ + ChangeFont(); +} + +/*! + * wxEVT_COMMAND_CHECKBOX_CLICKED event handler for wxID_FONTDIALOG_UNDERLINED + */ + +void wxFontDialog::OnFontdialogUnderlinedClick( wxCommandEvent& WXUNUSED(event) ) +{ + ChangeFont(); +} + +/*! + * wxEVT_COMMAND_BUTTON_CLICKED event handler for wxID_OK + */ + +void wxFontDialog::OnOkClick( wxCommandEvent& event ) +{ + event.Skip(); +} + + +/*! + * wxEVT_COMMAND_LISTBOX_SELECTED event handler for wxID_FONTDIALOG_FACENAME + */ + +void wxFontDialog::OnFontdialogFacenameSelected( wxCommandEvent& WXUNUSED(event) ) +{ + ChangeFont(); +} + +void wxFontDialog::OnColourChanged(wxCommandEvent & WXUNUSED(event)) +{ + m_fontData.SetColour(m_colourCtrl->GetColour()); + m_previewCtrl->SetForegroundColour(m_colourCtrl->GetColour()); + m_previewCtrl->Refresh(); +} + +/*! + * Should we show tooltips? + */ + +bool wxFontDialog::ShowToolTips() +{ + return true; +} + +void wxFontDialog::InitializeFont() +{ + int fontFamily = wxSWISS; + int fontWeight = wxNORMAL; + int fontStyle = wxNORMAL; + int fontSize = 12; + bool fontUnderline = false; + wxString fontName; + + if (m_fontData.m_initialFont.Ok()) + { + fontFamily = m_fontData.m_initialFont.GetFamily(); + fontWeight = m_fontData.m_initialFont.GetWeight(); + fontStyle = m_fontData.m_initialFont.GetStyle(); + fontSize = m_fontData.m_initialFont.GetPointSize(); + fontUnderline = m_fontData.m_initialFont.GetUnderlined(); + fontName = m_fontData.m_initialFont.GetFaceName(); + } + + m_dialogFont = wxFont(fontSize, fontFamily, fontStyle, + fontWeight, fontUnderline, fontName); + + if (m_previewCtrl) + m_previewCtrl->SetFont(m_dialogFont); + + m_fontData.SetChosenFont(m_dialogFont); +} + +/// Set controls according to current font +void wxFontDialog::InitializeControls() +{ + m_suppressUpdates = true; + + if (m_underlinedCtrl) + m_underlinedCtrl->SetValue(m_dialogFont.GetUnderlined()); + + m_boldCtrl->SetValue(m_dialogFont.GetWeight() == wxBOLD); + m_italicCtrl->SetValue(m_dialogFont.GetStyle() == wxITALIC); + m_sizeCtrl->SetValue(m_dialogFont.GetPointSize()); + + wxString facename = m_dialogFont.GetFaceName(); + if (facename.empty() || m_facenameCtrl->FindString(facename) == wxNOT_FOUND) + { + facename = FontFamilyIntToString(m_dialogFont.GetFamily()); + } + m_facenameCtrl->SetStringSelection(facename); + + if (m_colourCtrl && m_fontData.GetColour().Ok()) + { + m_colourCtrl->SetColour(m_fontData.GetColour()); + m_colourCtrl->Refresh(); + } + + m_suppressUpdates = false; +} + +/// Respond to font change +void wxFontDialog::ChangeFont() +{ + if (m_suppressUpdates) + return; + + bool underlined = m_underlinedCtrl ? m_underlinedCtrl->GetValue() : false; + bool italic = m_italicCtrl->GetValue(); + bool bold = m_boldCtrl->GetValue(); + int size = m_sizeCtrl->GetValue(); + wxString facename = m_facenameCtrl->GetStringSelection(); + + int family = FontFamilyStringToInt(facename); + if (family == -1) + family = wxDEFAULT; + else + facename = wxEmptyString; + + m_dialogFont = wxFont(size, family, italic ? wxITALIC : wxNORMAL, bold ? wxBOLD : wxNORMAL, + underlined, facename); + + m_fontData.SetChosenFont(m_dialogFont); + + m_previewCtrl->SetFont(m_dialogFont); + m_previewCtrl->Refresh(); +} + +void wxFontDialog::SetData(const wxFontData& fontdata) +{ + m_fontData = fontdata; +} + +bool wxFontDialog::IsShown() const +{ + return false; +} + +int wxFontDialog::ShowModal() +{ + return wxDialog::ShowModal(); +} + +void wxFontDialog::OnPanelClose() +{ +} + +const wxChar *FontFamilyIntToString(int family) +{ + switch (family) + { + case wxROMAN: + return _(""); + case wxDECORATIVE: + return _(""); + case wxMODERN: + return _(""); + case wxSCRIPT: + return _(""); + case wxTELETYPE: + return _(""); + case wxSWISS: + default: + return _(""); + } +} + +int FontFamilyStringToInt(const wxChar *family) +{ + if (!family) + return wxSWISS; + + if (wxStrcmp(family, _("")) == 0) + return wxROMAN; + else if (wxStrcmp(family, _("")) == 0) + return wxDECORATIVE; + else if (wxStrcmp(family, _("")) == 0) + return wxMODERN; + else if (wxStrcmp(family, _("")) == 0) + return wxSCRIPT; + else if (wxStrcmp(family, _("")) == 0) + return wxTELETYPE; + else if (wxStrcmp(family, _("")) == 0) + return wxSWISS; + else return -1; +} + +#endif // !USE_NATIVE_FONT_DIALOG_FOR_MACOSX + +#endif // wxMAC_USE_EXPERIMENTAL_FONTDIALOG + +#endif // wxUSE_FONTDLG diff --git a/src/osx/carbon/fontdlgosx.mm b/src/osx/carbon/fontdlgosx.mm new file mode 100644 index 0000000000..55071430b2 --- /dev/null +++ b/src/osx/carbon/fontdlgosx.mm @@ -0,0 +1,535 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/fontdlgosx.cpp +// Purpose: wxFontDialog class. +// Author: Ryan Norton +// Modified by: +// Created: 2004-10-03 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +// =========================================================================== +// declarations +// =========================================================================== + +// --------------------------------------------------------------------------- +// headers +// --------------------------------------------------------------------------- + +#include "wx/fontdlg.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/cmndata.h" +#endif + +#include "wx/fontutil.h" + +// ============================================================================ +// implementation +// ============================================================================ + + +#include "wx/cocoa/autorelease.h" +#include "wx/cocoa/string.h" + +#if wxMAC_USE_EXPERIMENTAL_FONTDIALOG + +#import +#import + +#include "wx/mac/uma.h" + +@interface wxMacFontPanelAccView : NSView +{ + BOOL m_okPressed ; + BOOL m_shouldClose ; + NSButton* m_cancelButton ; + NSButton* m_okButton ; +} + +- (IBAction)cancelPressed:(id)sender; +- (IBAction)okPressed:(id)sender; +- (void)resetFlags; +- (BOOL)closedWithOk; +- (BOOL)shouldCloseCarbon; +- (NSButton*)okButton; +@end + +@implementation wxMacFontPanelAccView : NSView +- (id)initWithFrame:(NSRect)rectBox +{ + [super initWithFrame:rectBox]; + + wxCFStringRef cfOkString( wxT("OK"), wxLocale::GetSystemEncoding() ); + wxCFStringRef cfCancelString( wxT("Cancel"), wxLocale::GetSystemEncoding() ); + + NSRect rectCancel = NSMakeRect( 10.0 , 10.0 , 82 , 24 ); + NSRect rectOK = NSMakeRect( 100.0 , 10.0 , 82 , 24 ); + + NSButton* cancelButton = [[NSButton alloc] initWithFrame:rectCancel]; + [cancelButton setTitle:(NSString*)wxCFRetain((CFStringRef)cfCancelString)]; + [cancelButton setBezelStyle:NSRoundedBezelStyle]; + [cancelButton setButtonType:NSMomentaryPushInButton]; + [cancelButton setAction:@selector(cancelPressed:)]; + [cancelButton setTarget:self]; + m_cancelButton = cancelButton ; + + NSButton* okButton = [[NSButton alloc] initWithFrame:rectOK]; + [okButton setTitle:(NSString*)wxCFRetain((CFStringRef)cfOkString)]; + [okButton setBezelStyle:NSRoundedBezelStyle]; + [okButton setButtonType:NSMomentaryPushInButton]; + [okButton setAction:@selector(okPressed:)]; + [okButton setTarget:self]; + // doesn't help either, the button is not highlighted after a color dialog has been used + // [okButton setKeyEquivalent:@"\r"]; + m_okButton = okButton ; + + + [self addSubview:cancelButton]; + [self addSubview:okButton]; + + [self resetFlags]; + return self; +} + +- (void)resetFlags +{ + m_okPressed = NO ; + m_shouldClose = NO ; +} + +- (IBAction)cancelPressed:(id)sender +{ + wxUnusedVar(sender); + m_shouldClose = YES ; + [NSApp stopModal]; +} + +- (IBAction)okPressed:(id)sender +{ + wxUnusedVar(sender); + m_okPressed = YES ; + m_shouldClose = YES ; + [NSApp stopModal]; +} + +-(BOOL)closedWithOk +{ + return m_okPressed ; +} + +-(BOOL)shouldCloseCarbon +{ + return m_shouldClose ; +} + +-(NSButton*)okButton +{ + return m_okButton ; +} +@end + + +extern "C" int RunMixedFontDialog(wxFontDialog* dialog) ; + +int RunMixedFontDialog(wxFontDialog* WXUNUSED(dialog)) +{ + int retval = wxID_CANCEL ; + + if ( !NSApplicationLoad() ) + { + wxFAIL_MSG("Couldn't load Cocoa in Carbon Environment"); + } + + wxAutoNSAutoreleasePool pool; + + // setting up the ok/cancel buttons + NSFontPanel* fontPanel = [NSFontPanel sharedFontPanel] ; + + // adjust modality for carbon environment + WindowRef carbonWindowRef = (WindowRef)[fontPanel windowRef] ; + SetWindowModality(carbonWindowRef, kWindowModalityAppModal , 0) ; + SetWindowGroup(carbonWindowRef , GetWindowGroupOfClass(kMovableModalWindowClass)); + + [fontPanel setFloatingPanel:NO] ; + [[fontPanel standardWindowButton:NSWindowCloseButton] setEnabled:NO] ; + + wxMacFontPanelAccView* accessoryView = (wxMacFontPanelAccView*) [fontPanel accessoryView] ; + if ( accessoryView == nil) + { + NSRect rectBox = NSMakeRect( 0 , 0 , 192 , 40 ); + accessoryView = [[wxMacFontPanelAccView alloc] initWithFrame:rectBox]; + [fontPanel setAccessoryView:accessoryView]; + + [fontPanel setDefaultButtonCell:[[accessoryView okButton] cell]] ; + } + + [accessoryView resetFlags]; + + NSModalSession session = [NSApp beginModalSessionForWindow:fontPanel]; + + [NSApp runModalSession:session]; + + [NSApp endModalSession:session]; + + // if we don't reenable it, FPShowHideFontPanel does not work + [[fontPanel standardWindowButton:NSWindowCloseButton] setEnabled:YES] ; + if( FPIsFontPanelVisible()) + FPShowHideFontPanel() ; + + if ( [accessoryView closedWithOk]) + { + retval = wxID_OK ; + } + + + return retval ; +} + +#else + +#if USE_NATIVE_FONT_DIALOG_FOR_MACOSX + +IMPLEMENT_DYNAMIC_CLASS(wxFontDialog, wxDialog) + +// Cocoa headers + +#import +#import +#import +#import +#import + +// --------------------------------------------------------------------------- +// wxWCDelegate - Window Closed delegate +// --------------------------------------------------------------------------- + +@interface wxWCDelegate : NSObject +{ + bool m_bIsClosed; +} + +// Delegate methods +- (id)init; +- (BOOL)windowShouldClose:(id)sender; +- (BOOL)isClosed; +@end // interface wxNSFontPanelDelegate : NSObject + +@implementation wxWCDelegate : NSObject + +- (id)init +{ + [super init]; + m_bIsClosed = false; + + return self; +} + +- (BOOL)windowShouldClose:(id)sender +{ + m_bIsClosed = true; + + [NSApp abortModal]; + [NSApp stopModal]; + return YES; +} + +- (BOOL)isClosed +{ + return m_bIsClosed; +} + +@end // wxNSFontPanelDelegate + +// --------------------------------------------------------------------------- +// wxWCODelegate - Window Closed or Open delegate +// --------------------------------------------------------------------------- + +@interface wxWCODelegate : NSObject +{ + bool m_bIsClosed; + bool m_bIsOpen; +} + +// Delegate methods +- (id)init; +- (BOOL)windowShouldClose:(id)sender; +- (void)windowDidUpdate:(NSNotification *)aNotification; +- (BOOL)isClosed; +- (BOOL)isOpen; +@end // interface wxNSFontPanelDelegate : NSObject + +@implementation wxWCODelegate : NSObject + +- (id)init +{ + [super init]; + m_bIsClosed = false; + m_bIsOpen = false; + + return self; +} + +- (BOOL)windowShouldClose:(id)sender +{ + m_bIsClosed = true; + m_bIsOpen = false; + + [NSApp abortModal]; + [NSApp stopModal]; + return YES; +} + +- (void)windowDidUpdate:(NSNotification *)aNotification +{ + if (m_bIsOpen == NO) + { + m_bIsClosed = false; + m_bIsOpen = true; + + [NSApp abortModal]; + [NSApp stopModal]; + } +} + +- (BOOL)isClosed +{ + return m_bIsClosed; +} + +- (BOOL)isOpen +{ + return m_bIsOpen; +} + +@end // wxNSFontPanelDelegate + +// --------------------------------------------------------------------------- +// wxFontDialog +// --------------------------------------------------------------------------- + +wxFontDialog::wxFontDialog() +{ +} + +wxFontDialog::wxFontDialog(wxWindow *parent, const wxFontData& data) +{ + Create(parent, data); +} + +wxFontDialog::~wxFontDialog() +{ +} + +bool wxFontDialog::Create(wxWindow *parent, const wxFontData& data) +{ + m_fontData = data; + + // + // This is the key call - this initializes + // events and window stuff for cocoa for carbon + // applications. + // + // This is also the only call here that is + // 10.2+ specific (the rest is OSX only), + // which, ironically, the carbon font + // panel requires. + // + bool bOK = NSApplicationLoad(); + + //autorelease pool - req'd for carbon + NSAutoreleasePool *thePool; + thePool = [[NSAutoreleasePool alloc] init]; + + //Get the initial wx font + wxFont& thewxfont = m_fontData.m_initialFont; + + //if the font is valid set the default (selected) font of the + //NSFontDialog to that font + if (thewxfont.Ok()) + { + NSFontTraitMask theMask = 0; + + if(thewxfont.GetStyle() == wxFONTSTYLE_ITALIC) + theMask |= NSItalicFontMask; + + if(thewxfont.IsFixedWidth()) + theMask |= NSFixedPitchFontMask; + + NSFont* theDefaultFont = + [[NSFontManager sharedFontManager] fontWithFamily: + wxNSStringWithWxString(thewxfont.GetFaceName()) + traits:theMask + weight:thewxfont.GetWeight() == wxBOLD ? 9 : + thewxfont.GetWeight() == wxLIGHT ? 0 : 5 + size: (float)(thewxfont.GetPointSize()) + ]; + + wxASSERT_MSG(theDefaultFont, wxT("Invalid default font for wxCocoaFontDialog!")); + + //Apple docs say to call NSFontManager::setSelectedFont + //However, 10.3 doesn't seem to create the font panel + //is this is done, so create it ourselves + [[NSFontPanel sharedFontPanel] setPanelFont:theDefaultFont isMultiple:NO]; + + } + + if(m_fontData.m_fontColour.Ok()) + [[NSColorPanel sharedColorPanel] setColor: + [NSColor colorWithCalibratedRed:m_fontData.m_fontColour.Red() / 255.0 + green:m_fontData.m_fontColour.Green() / 255.0 + blue:m_fontData.m_fontColour.Blue() / 255.0 + alpha:1.0] + ]; + else + [[NSColorPanel sharedColorPanel] setColor:[NSColor blackColor]]; + + //We're done - free up the pool + [thePool release]; + + return bOK; +} + +int wxFontDialog::ShowModal() +{ + //Start the pool. Required for carbon interaction + //(For those curious, the only thing that happens + //if you don't do this is a bunch of error + //messages about leaks on the console, + //with no windows shown or anything). + NSAutoreleasePool *thePool; + thePool = [[NSAutoreleasePool alloc] init]; + + //Get the shared color and font panel + NSFontPanel* theFontPanel = [NSFontPanel sharedFontPanel]; + NSColorPanel* theColorPanel = [NSColorPanel sharedColorPanel]; + + //Create and assign the delegates (cocoa event handlers) so + //we can tell if a window has closed/open or not + wxWCDelegate* theFPDelegate = [[wxWCDelegate alloc] init]; + [theFontPanel setDelegate:theFPDelegate]; + + wxWCODelegate* theCPDelegate = [[wxWCODelegate alloc] init]; + [theColorPanel setDelegate:theCPDelegate]; + + // + // Begin the modal loop for the font and color panels + // + // The idea is that we first make the font panel modal, + // but if the color panel is opened, unless we stop the + // modal loop the color panel opens behind the font panel + // with no input acceptable to it - which makes it useless. + // + // So we set up delegates for both the color and font panels, + // and the if the font panel opens the color panel, we + // stop the modal loop, and start a separate modal loop for + // the color panel until the color panel closes, switching + // back to the font panel modal loop once it does close. + // + do + { + // + // Start the font panel modal loop + // + NSModalSession session = [NSApp beginModalSessionForWindow:theFontPanel]; + for (;;) + { + [NSApp runModalSession:session]; + + //If the font panel is closed or the font panel + //opened the color panel, break + if ([theFPDelegate isClosed] || [theCPDelegate isOpen]) + break; + } + [NSApp endModalSession:session]; + + //is the color panel open? + if ([theCPDelegate isOpen]) + { + // + // Start the color panel modal loop + // + NSModalSession session = [NSApp beginModalSessionForWindow:theColorPanel]; + for (;;) + { + [NSApp runModalSession:session]; + + //If the color panel is closed, return the font panel modal loop + if ([theCPDelegate isClosed]) + break; + } + [NSApp endModalSession:session]; + } + //If the font panel is still alive (I.E. we broke + //out of its modal loop because the color panel was + //opened) return the font panel modal loop + }while([theFPDelegate isClosed] == NO); + + //free up the memory for the delegates - we don't need them anymore + [theFPDelegate release]; + [theCPDelegate release]; + + //Get the font the user selected + NSFont* theFont = [theFontPanel panelConvertFont:[NSFont userFontOfSize:0]]; + + //Get more information about the user's chosen font + NSFontTraitMask theTraits = [[NSFontManager sharedFontManager] traitsOfFont:theFont]; + int theFontWeight = [[NSFontManager sharedFontManager] weightOfFont:theFont]; + int theFontSize = (int) [theFont pointSize]; + + //Set the wx font to the appropriate data + if(theTraits & NSFixedPitchFontMask) + m_fontData.m_chosenFont.SetFamily(wxTELETYPE); + + m_fontData.m_chosenFont.SetFaceName(wxStringWithNSString([theFont familyName])); + m_fontData.m_chosenFont.SetPointSize(theFontSize); + m_fontData.m_chosenFont.SetStyle(theTraits & NSItalicFontMask ? wxFONTSTYLE_ITALIC : 0); + m_fontData.m_chosenFont.SetWeight(theFontWeight < 5 ? wxLIGHT : + theFontWeight >= 9 ? wxBOLD : wxNORMAL); + + //Get the shared color panel along with the chosen color and set the chosen color + NSColor* theColor = [[theColorPanel color] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + + m_fontData.m_fontColour.Set((unsigned char) ([theColor redComponent] * 255.0), + (unsigned char) ([theColor greenComponent] * 255.0), + (unsigned char) ([theColor blueComponent] * 255.0)); + + //Friendly debug stuff +#ifdef FONTDLGDEBUG + wxPrintf(wxT("---Font Panel---\n--NS--\nSize:%f\nWeight:%i\nTraits:%i\n--WX--\nFaceName:%s\nPointSize:%i\nStyle:%i\nWeight:%i\nColor:%i,%i,%i\n---END Font Panel---\n"), + + (float) theFontSize, + theFontWeight, + theTraits, + + m_fontData.m_chosenFont.GetFaceName().c_str(), + m_fontData.m_chosenFont.GetPointSize(), + m_fontData.m_chosenFont.GetStyle(), + m_fontData.m_chosenFont.GetWeight(), + m_fontData.m_fontColour.Red(), + m_fontData.m_fontColour.Green(), + m_fontData.m_fontColour.Blue() ); +#endif + + //Release the pool, we're done :) + [thePool release]; + + //Return ID_OK - there are no "apply" buttons or the like + //on either the font or color panel + return wxID_OK; +} + +//old api stuff (REMOVE ME) +bool wxFontDialog::IsShown() const +{ + return false; +} + +#endif // 10.2+ + +#endif diff --git a/src/osx/carbon/fontenum.cpp b/src/osx/carbon/fontenum.cpp new file mode 100644 index 0000000000..ce9718ed10 --- /dev/null +++ b/src/osx/carbon/fontenum.cpp @@ -0,0 +1,123 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/fontenum.cpp +// Purpose: wxFontEnumerator class for MacOS +// Author: Stefan Csomor +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_FONTENUM + +#include "wx/fontenum.h" + +#ifndef WX_PRECOMP + #include "wx/font.h" + #include "wx/intl.h" +#endif + +#include "wx/fontutil.h" +#include "wx/fontmap.h" +#include "wx/encinfo.h" + +#include "wx/mac/private.h" + +// ---------------------------------------------------------------------------- +// wxFontEnumerator +// ---------------------------------------------------------------------------- + +bool wxFontEnumerator::EnumerateFacenames(wxFontEncoding encoding, + bool fixedWidthOnly) +{ + if ( fixedWidthOnly ) + { + wxFAIL_MSG( "enumerating only fixed width fonts not supported" ); + return false; + } + + wxArrayString fontFamilies ; + +#if wxMAC_USE_ATSU_TEXT || wxMAC_USE_CORE_TEXT + + // + // From Apple's QA 1471 http://developer.apple.com/qa/qa2006/qa1471.html + // + + ATSFontFamilyIterator theFontFamilyIterator = NULL; + ATSFontFamilyRef theATSFontFamilyRef = 0; + OSStatus status = noErr; + + // Create the iterator + status = ATSFontFamilyIteratorCreate(kATSFontContextLocal, nil,nil, + kATSOptionFlagsUnRestrictedScope, + &theFontFamilyIterator ); + + wxUint32 macEncoding = wxMacGetSystemEncFromFontEnc(encoding) ; + + while (status == noErr) + { + // Get the next font in the iteration. + status = ATSFontFamilyIteratorNext( theFontFamilyIterator, &theATSFontFamilyRef ); + if(status == noErr) + { + #ifndef __LP64__ + // TODO CS : Find replacement + // added CS : avoid showing fonts that won't be displayable + FMFontStyle intrinsicStyle = 0 ; + FMFont fontInstance ; + FMFontFamily fmFamily = FMGetFontFamilyFromATSFontFamilyRef( theATSFontFamilyRef ); + status = FMGetFontFromFontFamilyInstance( fmFamily , 0 , &fontInstance , &intrinsicStyle); + if ( status != noErr ) + { + status = noErr; + continue ; + } +#endif + if ( encoding != wxFONTENCODING_SYSTEM ) + { + TextEncoding fontFamiliyEncoding = ATSFontFamilyGetEncoding(theATSFontFamilyRef) ; + if ( fontFamiliyEncoding != macEncoding ) + continue ; + } + + // TODO: determine fixed widths ... + + CFStringRef theName = NULL; + ATSFontFamilyGetName(theATSFontFamilyRef, kATSOptionFlagsDefault, &theName); + wxCFStringRef cfName(theName) ; + fontFamilies.Add(cfName.AsString(wxLocale::GetSystemEncoding())); + } + else if (status == kATSIterationScopeModified) // Make sure the font database hasnÕt changed. + { + // reset the iterator + status = ATSFontFamilyIteratorReset (kATSFontContextLocal, nil, nil, + kATSOptionFlagsUnRestrictedScope, + &theFontFamilyIterator); + fontFamilies.Clear() ; + } + } + ATSFontFamilyIteratorRelease(&theFontFamilyIterator); +#endif + + for ( size_t i = 0 ; i < fontFamilies.Count() ; ++i ) + { + if ( OnFacename( fontFamilies[i] ) == false ) + break ; + } + + return true; +} + +bool wxFontEnumerator::EnumerateEncodings(const wxString& WXUNUSED(family)) +{ + wxFAIL_MSG(wxT("wxFontEnumerator::EnumerateEncodings() not yet implemented")); + + return true; +} + +#endif // wxUSE_FONTENUM diff --git a/src/osx/carbon/fontutil.cpp b/src/osx/carbon/fontutil.cpp new file mode 100644 index 0000000000..5fc4804433 --- /dev/null +++ b/src/osx/carbon/fontutil.cpp @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: msw/fontutil.cpp +// Purpose: font-related helper functions for wxMSW +// Author: Vadim Zeitlin +// Modified by: +// Created: 05.11.99 +// RCS-ID: $Id$ +// Copyright: (c) 1999 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/string.h" + #include "wx/wxcrtvararg.h" + #include "wx/log.h" + #include "wx/intl.h" +#endif + +#include "wx/fontutil.h" +#include "wx/fontmap.h" +#include "wx/encinfo.h" +#include "wx/tokenzr.h" + + +// convert to/from the string representation: +// format is facename[;charset] +// +bool wxNativeEncodingInfo::FromString( const wxString& s ) +{ + wxStringTokenizer tokenizer(s, wxT(";")); + + facename = tokenizer.GetNextToken(); + if ( !facename ) + return false; + + wxString tmp = tokenizer.GetNextToken(); + if ( !tmp ) + { + // default charset (don't use DEFAULT_CHARSET though because of subtle + // Windows 9x/NT differences in handling it) + charset = 0; + } + else + { + if ( wxSscanf( tmp, wxT("%u"), &charset ) != 1 ) + // should be a number! + return false; + } + + return true; +} + +wxString wxNativeEncodingInfo::ToString() const +{ + wxString s(facename); + if ( charset != 0 ) + s << wxT(';') << charset; + + return s; +} + +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +bool wxGetNativeFontEncoding( wxFontEncoding encoding, wxNativeEncodingInfo *info ) +{ + wxCHECK_MSG( info, false, wxT("bad pointer in wxGetNativeFontEncoding") ); + + if ( encoding == wxFONTENCODING_DEFAULT ) + encoding = wxFont::GetDefaultEncoding(); + + info->encoding = encoding; + + return true; +} + +bool wxTestFontEncoding( const wxNativeEncodingInfo& WXUNUSED(info) ) +{ + // basically we should be able to support every encoding via the OS + return true; +} diff --git a/src/osx/carbon/frame.cpp b/src/osx/carbon/frame.cpp new file mode 100644 index 0000000000..d17d5d8147 --- /dev/null +++ b/src/osx/carbon/frame.cpp @@ -0,0 +1,402 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/frame.cpp +// Purpose: wxFrame +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/frame.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/dcclient.h" + #include "wx/menu.h" + #include "wx/dialog.h" + #include "wx/settings.h" + #include "wx/toolbar.h" + #include "wx/statusbr.h" + #include "wx/menuitem.h" +#endif // WX_PRECOMP + +#include "wx/mac/uma.h" + +BEGIN_EVENT_TABLE(wxFrame, wxFrameBase) + EVT_ACTIVATE(wxFrame::OnActivate) + // EVT_MENU_HIGHLIGHT_ALL(wxFrame::OnMenuHighlight) + EVT_SYS_COLOUR_CHANGED(wxFrame::OnSysColourChanged) +// EVT_IDLE(wxFrame::OnIdle) +// EVT_CLOSE(wxFrame::OnCloseWindow) +END_EVENT_TABLE() + +IMPLEMENT_DYNAMIC_CLASS(wxFrame, wxTopLevelWindow) + +#define WX_MAC_STATUSBAR_HEIGHT 18 + +// ---------------------------------------------------------------------------- +// creation/destruction +// ---------------------------------------------------------------------------- + +void wxFrame::Init() +{ + m_winLastFocused = NULL; +} + +bool wxFrame::Create(wxWindow *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) +{ + + if ( !wxTopLevelWindow::Create(parent, id, title, pos, size, style, name) ) + return false; + + return true; +} + +wxFrame::~wxFrame() +{ + m_isBeingDeleted = true; + DeleteAllBars(); +} + +// get the origin of the client area in the client coordinates +wxPoint wxFrame::GetClientAreaOrigin() const +{ + wxPoint pt = wxTopLevelWindow::GetClientAreaOrigin(); + +#if wxUSE_TOOLBAR && !defined(__WXUNIVERSAL__) + wxToolBar *toolbar = GetToolBar(); + if ( toolbar && toolbar->IsShown() ) + { + int w, h; + toolbar->GetSize(&w, &h); + + if ( toolbar->GetWindowStyleFlag() & wxTB_VERTICAL ) + { + pt.x += w; + } + else + { +#if !wxMAC_USE_NATIVE_TOOLBAR + pt.y += h; +#endif + } + } +#endif + + return pt; +} + +bool wxFrame::Enable(bool enable) +{ + if ( !wxWindow::Enable(enable) ) + return false; + + if ( m_frameMenuBar && m_frameMenuBar == wxMenuBar::MacGetInstalledMenuBar() ) + { + int iMaxMenu = m_frameMenuBar->GetMenuCount(); + for ( int i = 0 ; i < iMaxMenu ; ++ i ) + { + m_frameMenuBar->EnableTop( i , enable ) ; + } + } + + return true; +} + +#if wxUSE_STATUSBAR +wxStatusBar *wxFrame::OnCreateStatusBar(int number, long style, wxWindowID id, + const wxString& name) +{ + wxStatusBar *statusBar; + + statusBar = new wxStatusBar(this, id, style, name); + statusBar->SetSize(100, WX_MAC_STATUSBAR_HEIGHT); + statusBar->SetFieldsCount(number); + + return statusBar; +} + +void wxFrame::PositionStatusBar() +{ + if (m_frameStatusBar && m_frameStatusBar->IsShown() ) + { + int w, h; + GetClientSize(&w, &h); + + // Since we wish the status bar to be directly under the client area, + // we use the adjusted sizes without using wxSIZE_NO_ADJUSTMENTS. + m_frameStatusBar->SetSize(0, h, w, WX_MAC_STATUSBAR_HEIGHT); + } +} +#endif // wxUSE_STATUSBAR + +// Responds to colour changes, and passes event on to children. +void wxFrame::OnSysColourChanged(wxSysColourChangedEvent& event) +{ + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE)); + Refresh(); + +#if wxUSE_STATUSBAR + if ( m_frameStatusBar ) + { + wxSysColourChangedEvent event2; + + event2.SetEventObject( m_frameStatusBar ); + m_frameStatusBar->ProcessEvent(event2); + } +#endif // wxUSE_STATUSBAR + + // Propagate the event to the non-top-level children + wxWindow::OnSysColourChanged(event); +} + +// Default activation behaviour - set the focus for the first child +// subwindow found. +void wxFrame::OnActivate(wxActivateEvent& event) +{ + if ( !event.GetActive() ) + { + // remember the last focused child if it is our child + m_winLastFocused = FindFocus(); + + // so we NULL it out if it's a child from some other frame + wxWindow *win = m_winLastFocused; + while ( win ) + { + if ( win->IsTopLevel() ) + { + if ( win != this ) + m_winLastFocused = NULL; + + break; + } + + win = win->GetParent(); + } + + event.Skip(); + } + else + { + // restore focus to the child which was last focused + wxWindow *parent = m_winLastFocused + ? m_winLastFocused->GetParent() + : NULL; + + if (parent == NULL) + parent = this; + + wxSetFocusToChild(parent, &m_winLastFocused); + + if (m_frameMenuBar != NULL) + { + m_frameMenuBar->MacInstallMenuBar(); + } + else + { + wxFrame *tlf = wxDynamicCast( wxTheApp->GetTopWindow(), wxFrame ); + if (tlf != NULL) + { + // Trying top-level frame membar + if (tlf->GetMenuBar()) + tlf->GetMenuBar()->MacInstallMenuBar(); + } + } + } +} + +void wxFrame::DetachMenuBar() +{ + if ( m_frameMenuBar ) + m_frameMenuBar->UnsetInvokingWindow(); + + wxFrameBase::DetachMenuBar(); +} + +void wxFrame::AttachMenuBar( wxMenuBar *menuBar ) +{ + wxFrame* tlf = wxDynamicCast( wxFindWinFromMacWindow( FrontNonFloatingWindow() ) , wxFrame ); + bool makeCurrent = false; + + // if this is already the current menubar or we are the frontmost window + if ( (tlf == this) || (m_frameMenuBar == wxMenuBar::MacGetInstalledMenuBar()) ) + makeCurrent = true; + // or there is an app-level menubar like MDI + else if ( tlf && (tlf->GetMenuBar() == NULL) && (((wxFrame*)wxTheApp->GetTopWindow()) == this) ) + makeCurrent = true; + + wxFrameBase::AttachMenuBar( menuBar ); + + if (m_frameMenuBar) + { + m_frameMenuBar->SetInvokingWindow( this ); + if (makeCurrent) + m_frameMenuBar->MacInstallMenuBar(); + } +} + +void wxFrame::DoGetClientSize(int *x, int *y) const +{ + wxTopLevelWindow::DoGetClientSize( x , y ); + +#if wxUSE_STATUSBAR + if ( GetStatusBar() && GetStatusBar()->IsShown() && y ) + *y -= WX_MAC_STATUSBAR_HEIGHT; +#endif + +#if wxUSE_TOOLBAR + wxToolBar *toolbar = GetToolBar(); + if ( toolbar && toolbar->IsShown() ) + { + int w, h; + toolbar->GetSize(&w, &h); + + if ( toolbar->GetWindowStyleFlag() & wxTB_VERTICAL ) + { + if ( x ) + *x -= w; + } + else + { +#if !wxMAC_USE_NATIVE_TOOLBAR + if ( y ) + *y -= h; +#endif + } + } +#endif +} + +bool wxFrame::MacIsChildOfClientArea( const wxWindow* child ) const +{ +#if wxUSE_STATUSBAR + if ( child == GetStatusBar() ) + return false ; +#endif + +#if wxUSE_TOOLBAR + if ( child == GetToolBar() ) + return false ; +#endif + + return wxFrameBase::MacIsChildOfClientArea( child ) ; +} + +void wxFrame::DoSetClientSize(int clientwidth, int clientheight) +{ + int currentclientwidth , currentclientheight ; + int currentwidth , currentheight ; + + GetClientSize( ¤tclientwidth , ¤tclientheight ) ; + if ( clientwidth == -1 ) + clientwidth = currentclientwidth ; + if ( clientheight == -1 ) + clientheight = currentclientheight ; + GetSize( ¤twidth , ¤theight ) ; + + // find the current client size + + // Find the difference between the entire window (title bar and all) and + // the client area; add this to the new client size to move the window + DoSetSize( -1 , -1 , currentwidth + clientwidth - currentclientwidth , + currentheight + clientheight - currentclientheight , wxSIZE_USE_EXISTING ) ; +} + +#if wxUSE_TOOLBAR +void wxFrame::SetToolBar(wxToolBar *toolbar) +{ + if ( m_frameToolBar == toolbar ) + return ; + +#if wxMAC_USE_NATIVE_TOOLBAR + if ( m_frameToolBar ) + m_frameToolBar->MacInstallNativeToolbar( false ) ; +#endif + + m_frameToolBar = toolbar ; + +#if wxMAC_USE_NATIVE_TOOLBAR + if ( toolbar ) + toolbar->MacInstallNativeToolbar( true ) ; +#endif +} + +wxToolBar* wxFrame::CreateToolBar(long style, wxWindowID id, const wxString& name) +{ + if ( wxFrameBase::CreateToolBar(style, id, name) ) + PositionToolBar(); + + return m_frameToolBar; +} + +void wxFrame::PositionToolBar() +{ + int cw, ch; + + GetSize( &cw , &ch ) ; + + int statusX = 0 ; + int statusY = 0 ; + +#if wxUSE_STATUSBAR + if (GetStatusBar() && GetStatusBar()->IsShown()) + { + GetStatusBar()->GetClientSize(&statusX, &statusY); + ch -= statusY; + } +#endif + + if (GetToolBar()) + { + int tx, ty, tw, th; + + tx = ty = 0 ; + GetToolBar()->GetSize(&tw, &th); + if (GetToolBar()->GetWindowStyleFlag() & wxTB_VERTICAL) + { + // Use the 'real' position. wxSIZE_NO_ADJUSTMENTS + // means, pretend we don't have toolbar/status bar, so we + // have the original client size. + GetToolBar()->SetSize(tx , ty , tw, ch , wxSIZE_NO_ADJUSTMENTS ); + } + else if (GetToolBar()->GetWindowStyleFlag() & wxTB_BOTTOM) + { + //FIXME: this positions the tool bar almost correctly, but still it doesn't work right yet, + //as 1) the space for the 'old' top toolbar is still taken up, and 2) the toolbar + //doesn't extend it's width to the width of the frame. + tx = 0; + ty = ch - (th + statusY); + GetToolBar()->SetSize(tx, ty, cw, th, wxSIZE_NO_ADJUSTMENTS ); + } + else + { +#if !wxMAC_USE_NATIVE_TOOLBAR + // Use the 'real' position + GetToolBar()->SetSize(tx , ty , cw , th, wxSIZE_NO_ADJUSTMENTS ); +#endif + } + } +} +#endif // wxUSE_TOOLBAR + +void wxFrame::PositionBars() +{ +#if wxUSE_STATUSBAR + PositionStatusBar(); +#endif +#if wxUSE_TOOLBAR + PositionToolBar(); +#endif +} + + diff --git a/src/osx/carbon/gauge.cpp b/src/osx/carbon/gauge.cpp new file mode 100644 index 0000000000..b2f36abe95 --- /dev/null +++ b/src/osx/carbon/gauge.cpp @@ -0,0 +1,128 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: gauge.cpp +// Purpose: wxGauge class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_GAUGE + +#include "wx/gauge.h" + +IMPLEMENT_DYNAMIC_CLASS(wxGauge, wxControl) + +#include "wx/mac/uma.h" + +bool wxGauge::Create( wxWindow *parent, + wxWindowID id, + int range, + const wxPoint& pos, + const wxSize& s, + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_macIsUserPane = false; + + if ( !wxGaugeBase::Create( parent, id, range, pos, s, style & 0xE0FFFFFF, validator, name ) ) + return false; + + wxSize size = s; + +#if 0 + if (size.x == wxDefaultCoord && size.y == wxDefaultCoord) + size = wxSize( 200 , 16 ); +#endif + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + m_peer = new wxMacControl( this ); + OSStatus err = CreateProgressBarControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, + GetValue(), 0, GetRange(), false /* not indeterminate */, m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + if ( GetValue() == 0 ) + m_peer->SetData( kControlEntireControl, kControlProgressBarAnimatingTag, (Boolean)false ); + + MacPostControlCreate( pos, size ); + + return true; +} + +void wxGauge::SetRange(int r) +{ + // we are going via the base class in case there is + // some change behind the values by it + wxGaugeBase::SetRange( r ) ; + if ( m_peer && m_peer->Ok() ){ + // switch back to determinate mode if not there already + if ( m_peer->GetData( kControlNoPart, kControlProgressBarIndeterminateTag ) != false ) + { + m_peer->SetData( kControlNoPart, kControlProgressBarIndeterminateTag, (Boolean)false ); + } + + m_peer->SetMaximum( GetRange() ) ; + } +} + +void wxGauge::SetValue(int pos) +{ + // we are going via the base class in case there is + // some change behind the values by it + wxGaugeBase::SetValue( pos ) ; + + if ( m_peer && m_peer->Ok() ) + { + // switch back to determinate mode if not there already + if ( m_peer->GetData( kControlNoPart, kControlProgressBarIndeterminateTag ) != false ) + { + m_peer->SetData( kControlNoPart, kControlProgressBarIndeterminateTag, (Boolean)false ); + } + + m_peer->SetValue( GetValue() ) ; + + // turn off animation in the unnecessary situations as this is consuming a lot of CPU otherwise + Boolean shouldAnimate = ( GetValue() > 0 && GetValue() < GetRange() ) ; + if ( m_peer->GetData( kControlEntireControl, kControlProgressBarAnimatingTag ) != shouldAnimate ) + { + m_peer->SetData( kControlEntireControl, kControlProgressBarAnimatingTag, shouldAnimate ) ; + if ( !shouldAnimate ) + Refresh() ; + } + } +} + +int wxGauge::GetValue() const +{ +#if 0 + if ( m_peer && m_peer->Ok() ) + return m_peer->GetValue() ; +#endif + + return m_gaugePos ; +} + +void wxGauge::Pulse() +{ + if ( m_peer && m_peer->Ok() ) + { + if ( m_peer->GetData( kControlNoPart, kControlProgressBarIndeterminateTag ) != true ) + { + m_peer->SetData( kControlNoPart, kControlProgressBarIndeterminateTag, true); + } + + if ( m_peer->GetData( kControlEntireControl, kControlProgressBarAnimatingTag ) != true ) + { + m_peer->SetData( kControlEntireControl, kControlProgressBarAnimatingTag, true ) ; + } + } +} + +#endif // wxUSE_GAUGE + diff --git a/src/osx/carbon/gdiobj.cpp b/src/osx/carbon/gdiobj.cpp new file mode 100644 index 0000000000..76156134e0 --- /dev/null +++ b/src/osx/carbon/gdiobj.cpp @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/gdiobj.cpp +// Purpose: wxGDIObject class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/gdiobj.h" + +#ifndef WX_PRECOMP + #include "wx/gdicmn.h" + #include "wx/module.h" +#endif + +#include "wx/link.h" +#include "wx/mac/private.h" + +// Linker will discard entire object file without this +wxFORCE_LINK_THIS_MODULE(gdiobj) + +class wxStockGDIMac: public wxStockGDI, public wxModule +{ +public: + virtual const wxFont* GetFont(Item item); + + virtual bool OnInit(); + virtual void OnExit(); + +private: + typedef wxStockGDI super; + DECLARE_DYNAMIC_CLASS(wxStockGDIMac) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxStockGDIMac, wxModule) + +bool wxStockGDIMac::OnInit() +{ + // Override default instance + ms_instance = this; + return true; +} + +void wxStockGDIMac::OnExit() +{ +} + +const wxFont* wxStockGDIMac::GetFont(Item item) +{ + wxFont* font = static_cast(ms_stockObject[item]); + if (font == NULL) + { + switch (item) + { + case FONT_NORMAL: + font = new wxFont; + font->MacCreateFromThemeFont(kThemeSystemFont); + break; + case FONT_SMALL: + font = new wxFont; + font->MacCreateFromThemeFont(kThemeSmallSystemFont); + break; + default: + font = const_cast(super::GetFont(item)); + break; + } + ms_stockObject[item] = font; + } + return font; +} diff --git a/src/osx/carbon/glcanvas.cpp b/src/osx/carbon/glcanvas.cpp new file mode 100644 index 0000000000..7d265bc1d6 --- /dev/null +++ b/src/osx/carbon/glcanvas.cpp @@ -0,0 +1,499 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/glcanvas.cpp +// Purpose: wxGLCanvas, for using OpenGL with wxWidgets under Macintosh +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "wx/wxprec.h" + +#if defined(__BORLANDC__) + #pragma hdrstop +#endif + +#if wxUSE_GLCANVAS + +#include "wx/glcanvas.h" + +#ifndef WX_PRECOMP + #include "wx/frame.h" + #include "wx/log.h" + #include "wx/settings.h" +#endif + +#include "wx/mac/uma.h" + +#include "wx/mac/private.h" + +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +static void wxLogAGLError(const char *func) +{ + const int err = aglGetError(); + + wxLogError(_("OpenGL function \"%s\" failed: %s (error %d)"), + func, aglErrorString(err), err); +} + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxGLContext +// ---------------------------------------------------------------------------- + +wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext *other) +{ + m_aglContext = aglCreateContext(win->GetAGLPixelFormat(), + other ? other->m_aglContext : NULL); + if ( !m_aglContext ) + wxLogAGLError("aglCreateContext"); +} + +wxGLContext::~wxGLContext() +{ + if ( m_aglContext ) + { + // it's ok to pass the current context to this function + if ( !aglDestroyContext(m_aglContext) ) + wxLogAGLError("aglDestroyContext"); + } +} + +bool wxGLContext::SetCurrent(const wxGLCanvas& win) const +{ + if ( !m_aglContext ) + return false; + + AGLDrawable drawable = (AGLDrawable)GetWindowPort( + MAC_WXHWND(win.MacGetTopLevelWindowRef())); + if ( !aglSetDrawable(m_aglContext, drawable) ) + { + wxLogAGLError("aglSetDrawable"); + return false; + } + + if ( !aglSetCurrentContext(m_aglContext) ) + { + wxLogAGLError("aglSetCurrentContext"); + return false; + } + + wx_const_cast(wxGLCanvas&, win).SetViewport(); + return true; +} + +// ---------------------------------------------------------------------------- +// wxGLCanvas +// ---------------------------------------------------------------------------- + +IMPLEMENT_CLASS(wxGLCanvas, wxWindow) + +BEGIN_EVENT_TABLE(wxGLCanvas, wxWindow) + EVT_SIZE(wxGLCanvas::OnSize) +END_EVENT_TABLE() + +wxGLCanvas::wxGLCanvas(wxWindow *parent, + wxWindowID id, + const int *attribList, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const wxPalette& palette) +{ + Create(parent, id, pos, size, style, name, attribList, palette); +} + +#if WXWIN_COMPATIBILITY_2_8 + +wxGLCanvas::wxGLCanvas(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& palette) +{ + if ( Create(parent, id, pos, size, style, name, attribList, palette) ) + m_glContext = new wxGLContext(this); +} + +wxGLCanvas::wxGLCanvas(wxWindow *parent, + const wxGLContext *shared, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& palette) +{ + if ( Create(parent, id, pos, size, style, name, attribList, palette) ) + m_glContext = new wxGLContext(this, shared); +} + +wxGLCanvas::wxGLCanvas(wxWindow *parent, + const wxGLCanvas *shared, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& palette) +{ + if ( Create(parent, id, pos, size, style, name, attribList, palette) ) + m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL); +} + +#endif // WXWIN_COMPATIBILITY_2_8 + +/* static */ +bool wxGLCanvasBase::IsExtensionSupported(const char *extension) +{ + // we need a valid context to query for extensions. + const GLint defaultAttribs[] = { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_NONE }; + AGLPixelFormat fmt = aglChoosePixelFormat(NULL, 0, defaultAttribs); + AGLContext ctx = aglCreateContext(fmt, NULL); + if ( !ctx ) + return false; + + wxString extensions = wxString::FromAscii(glGetString(GL_EXTENSIONS)); + + aglDestroyPixelFormat(fmt); + aglDestroyContext(ctx); + + return IsExtensionInList(extensions, extension); +} + +/* static */ +bool wxGLCanvas::IsAGLMultiSampleAvailable() +{ + static int s_isMultiSampleAvailable = -1; + if ( s_isMultiSampleAvailable == -1 ) + s_isMultiSampleAvailable = IsExtensionSupported("GL_ARB_multisample"); + + return s_isMultiSampleAvailable != 0; +} + +static AGLPixelFormat ChoosePixelFormat(const int *attribList) +{ + GLint data[512]; + const GLint defaultAttribs[] = + { + AGL_RGBA, + AGL_DOUBLEBUFFER, + AGL_MINIMUM_POLICY, // never choose less than requested + AGL_DEPTH_SIZE, 1, // use largest available depth buffer + AGL_RED_SIZE, 1, + AGL_GREEN_SIZE, 1, + AGL_BLUE_SIZE, 1, + AGL_ALPHA_SIZE, 0, + AGL_NONE + }; + + const GLint *attribs; + if ( !attribList ) + { + attribs = defaultAttribs; + } + else + { + unsigned p = 0; + data[p++] = AGL_MINIMUM_POLICY; // make _SIZE tags behave more like GLX + + for ( unsigned arg = 0; attribList[arg] !=0 && p < WXSIZEOF(data); ) + { + switch ( attribList[arg++] ) + { + case WX_GL_RGBA: + data[p++] = AGL_RGBA; + break; + + case WX_GL_BUFFER_SIZE: + data[p++] = AGL_BUFFER_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_LEVEL: + data[p++]=AGL_LEVEL; + data[p++]=attribList[arg++]; + break; + + case WX_GL_DOUBLEBUFFER: + data[p++] = AGL_DOUBLEBUFFER; + break; + + case WX_GL_STEREO: + data[p++] = AGL_STEREO; + break; + + case WX_GL_AUX_BUFFERS: + data[p++] = AGL_AUX_BUFFERS; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_RED: + data[p++] = AGL_RED_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_GREEN: + data[p++] = AGL_GREEN_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_BLUE: + data[p++] = AGL_BLUE_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_ALPHA: + data[p++] = AGL_ALPHA_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_DEPTH_SIZE: + data[p++] = AGL_DEPTH_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_STENCIL_SIZE: + data[p++] = AGL_STENCIL_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_ACCUM_RED: + data[p++] = AGL_ACCUM_RED_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_ACCUM_GREEN: + data[p++] = AGL_ACCUM_GREEN_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_ACCUM_BLUE: + data[p++] = AGL_ACCUM_BLUE_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_MIN_ACCUM_ALPHA: + data[p++] = AGL_ACCUM_ALPHA_SIZE; + data[p++] = attribList[arg++]; + break; + + case WX_GL_SAMPLE_BUFFERS: + if ( !wxGLCanvas::IsAGLMultiSampleAvailable() ) + { + if ( !attribList[arg++] ) + break; + + return false; + } + + data[p++] = AGL_SAMPLE_BUFFERS_ARB; + if ( (data[p++] = attribList[arg++]) == true ) + { + // don't use software fallback + data[p++] = AGL_NO_RECOVERY; + } + break; + + case WX_GL_SAMPLES: + if ( !wxGLCanvas::IsAGLMultiSampleAvailable() ) + { + if ( !attribList[arg++] ) + break; + + return false; + } + + data[p++] = AGL_SAMPLES_ARB; + data[p++] = attribList[arg++]; + break; + } + } + + data[p] = AGL_NONE; + + attribs = data; + } + + return aglChoosePixelFormat(NULL, 0, attribs); +} + +bool wxGLCanvas::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& WXUNUSED(palette)) +{ + m_needsUpdate = false; + m_macCanvasIsShown = false; + + m_aglFormat = ChoosePixelFormat(attribList); + if ( !m_aglFormat ) + return false; + + if ( !wxWindow::Create(parent, id, pos, size, style, name) ) + return false; + + m_macCanvasIsShown = true; + + return true; +} + +wxGLCanvas::~wxGLCanvas() +{ + if ( m_aglFormat ) + aglDestroyPixelFormat(m_aglFormat); +} + +/* static */ +bool wxGLCanvasBase::IsDisplaySupported(const int *attribList) +{ + AGLPixelFormat aglFormat = ChoosePixelFormat(attribList); + + if ( !aglFormat ) + return false; + + aglDestroyPixelFormat(aglFormat); + + return true; +} + +bool wxGLCanvas::SwapBuffers() +{ + AGLContext context = aglGetCurrentContext(); + wxCHECK_MSG(context, false, _T("should have current context")); + + aglSwapBuffers(context); + return true; +} + +void wxGLCanvas::SetViewport() +{ + if ( !m_needsUpdate ) + return; + + m_needsUpdate = false; + + AGLContext context = aglGetCurrentContext(); + if ( !context ) + return; + + // viewport is initially set to entire port, adjust it to just this window + int x = 0, + y = 0; + MacClientToRootWindow(&x , &y); + + int width, height; + GetClientSize(&width, &height); + + Rect bounds; + GetWindowPortBounds(MAC_WXHWND(MacGetTopLevelWindowRef()) , &bounds); + +#if 0 + // TODO in case we adopt point vs pixel coordinates, this will make the conversion + HIRect hiRect = CGRectMake( x, y, width, height ); + HIRectConvert( &hiRect, kHICoordSpace72DPIGlobal, NULL, kHICoordSpaceScreenPixel, NULL); + HIRect hiBounds = CGRectMake( 0, 0, bounds.right - bounds.left , bounds.bottom - bounds.top ); + HIRectConvert( &hiBounds, kHICoordSpace72DPIGlobal, NULL, kHICoordSpaceScreenPixel, NULL); + GLint parms[4]; + parms[0] = hiRect.origin.x; + parms[1] = hiBounds.size.height - (hiRect.origin.y + hiRect.size.height); + parms[2] = hiRect.size.width; + parms[3] = hiRect.size.height; +#else + GLint parms[4]; + parms[0] = x; + parms[1] = bounds.bottom - bounds.top - ( y + height ); + parms[2] = width; + parms[3] = height; +#endif + + // move the buffer rect out of sight if we're hidden + if ( !m_macCanvasIsShown ) + parms[0] += 20000; + + if ( !aglSetInteger(context, AGL_BUFFER_RECT, parms) ) + wxLogAGLError("aglSetInteger(AGL_BUFFER_RECT)"); + + if ( !aglEnable(context, AGL_BUFFER_RECT) ) + wxLogAGLError("aglEnable(AGL_BUFFER_RECT)"); + + if ( !aglUpdateContext(context) ) + wxLogAGLError("aglUpdateContext"); +} + +void wxGLCanvas::OnSize(wxSizeEvent& event) +{ + MacUpdateView(); + event.Skip(); +} + +void wxGLCanvas::MacUpdateView() +{ + m_needsUpdate = true; + Refresh(false); +} + +void wxGLCanvas::MacSuperChangedPosition() +{ + MacUpdateView(); + wxWindow::MacSuperChangedPosition(); +} + +void wxGLCanvas::MacTopLevelWindowChangedPosition() +{ + MacUpdateView(); + wxWindow::MacTopLevelWindowChangedPosition(); +} + +void wxGLCanvas::MacVisibilityChanged() +{ + if ( IsShownOnScreen() != m_macCanvasIsShown ) + { + m_macCanvasIsShown = !m_macCanvasIsShown; + MacUpdateView(); + } + + wxWindowMac::MacVisibilityChanged(); +} + +// ---------------------------------------------------------------------------- +// wxGLApp +// ---------------------------------------------------------------------------- + +bool wxGLApp::InitGLVisual(const int *attribList) +{ + AGLPixelFormat fmt = ChoosePixelFormat(attribList); + if ( !fmt ) + return false; + + aglDestroyPixelFormat(fmt); + return true; +} + +#endif // wxUSE_GLCANVAS diff --git a/src/osx/carbon/graphics.cpp b/src/osx/carbon/graphics.cpp new file mode 100644 index 0000000000..3c65a99a8c --- /dev/null +++ b/src/osx/carbon/graphics.cpp @@ -0,0 +1,2594 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/dccg.cpp +// Purpose: wxDC class +// Author: Stefan Csomor +// Modified by: +// Created: 01/02/97 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/graphics.h" +#include "wx/private/graphics.h" + +#ifndef WX_PRECOMP + #include "wx/dcclient.h" + #include "wx/dcmemory.h" + #include "wx/dcprint.h" + #include "wx/log.h" + #include "wx/region.h" + #include "wx/image.h" + #include "wx/icon.h" +#endif + + +#ifdef __MSL__ + #if __MSL__ >= 0x6000 + #include "math.h" + // in case our functions were defined outside std, we make it known all the same + namespace std { } + using namespace std; + #endif +#endif + +#ifdef __WXMAC__ + #include "wx/mac/uma.h" + #include "wx/mac/dcprint.h" +#else + #include "CoreServices/CoreServices.h" + #include "ApplicationServices/ApplicationServices.h" + #include "wx/mac/corefoundation/cfstring.h" + #include "wx/cocoa/dcclient.h" +#endif + +#ifdef __WXCOCOA__ + +CGColorSpaceRef wxMacGetGenericRGBColorSpace() +{ + static wxCFRef genericRGBColorSpace; + + if (genericRGBColorSpace == NULL) + { + genericRGBColorSpace.reset( CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ) ); + } + + return genericRGBColorSpace; +} + +int UMAGetSystemVersion() +{ + return 0x1050; +} + + +#define wxMAC_USE_CORE_TEXT 1 + +#endif + +//----------------------------------------------------------------------------- +// constants +//----------------------------------------------------------------------------- + +#if !defined( __DARWIN__ ) || defined(__MWERKS__) +#ifndef M_PI +const double M_PI = 3.14159265358979; +#endif +#endif + +static const double RAD2DEG = 180.0 / M_PI; + +// +// Pen, Brushes and Fonts +// + +#pragma mark - +#pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes + +OSStatus wxMacDrawCGImage( + CGContextRef inContext, + const CGRect * inBounds, + CGImageRef inImage) +{ +#if defined( __LP64__ ) || defined(__WXCOCOA__) + // todo flip + CGContextDrawImage(inContext, *inBounds, inImage ); + return noErr; +#else + return HIViewDrawCGImage( inContext, inBounds, inImage ); +#endif +} + +CGColorRef wxMacCreateCGColor( const wxColour& col ) +{ + CGColorRef retval = 0; +#ifdef __WXMAC__ + retval = col.CreateCGColor(); +#else +// TODO add conversion NSColor - CGColorRef (obj-c) +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( CGColorCreateGenericRGB ) + retval = CGColorCreateGenericRGB( col.Red() / 255.0 , col.Green() / 255.0, col.Blue() / 255.0, col.Alpha() / 255.0 ); + else +#endif + { + CGFloat components[4] = { col.Red() / 255.0, col.Green() / 255.0, col.Blue() / 255.0, col.Alpha() / 255.0 } ; + retval = CGColorCreate( wxMacGetGenericRGBColorSpace() , components ) ; + } + +#endif + return retval; +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 && defined(wxMAC_USE_CORE_TEXT) + +CTFontRef wxMacCreateCTFont( const wxFont& font ) +{ +#ifdef __WXMAC__ + return wxCFRetain((CTFontRef) font.MacGetCTFont()); +#else + return CTFontCreateWithName( wxCFStringRef( font.GetFaceName(), wxLocale::GetSystemEncoding() ) , font.GetPointSize() , NULL ); +#endif +} + +#endif + +// CGPattern wrapper class: always allocate on heap, never call destructor + +class wxMacCoreGraphicsPattern +{ +public : + wxMacCoreGraphicsPattern() {} + + // is guaranteed to be called only with a non-Null CGContextRef + virtual void Render( CGContextRef ctxRef ) = 0; + + operator CGPatternRef() const { return m_patternRef; } + +protected : + virtual ~wxMacCoreGraphicsPattern() + { + // as this is called only when the m_patternRef is been released; + // don't release it again + } + + static void _Render( void *info, CGContextRef ctxRef ) + { + wxMacCoreGraphicsPattern* self = (wxMacCoreGraphicsPattern*) info; + if ( self && ctxRef ) + self->Render( ctxRef ); + } + + static void _Dispose( void *info ) + { + wxMacCoreGraphicsPattern* self = (wxMacCoreGraphicsPattern*) info; + delete self; + } + + CGPatternRef m_patternRef; + + static const CGPatternCallbacks ms_Callbacks; +}; + +const CGPatternCallbacks wxMacCoreGraphicsPattern::ms_Callbacks = { 0, &wxMacCoreGraphicsPattern::_Render, &wxMacCoreGraphicsPattern::_Dispose }; + +class ImagePattern : public wxMacCoreGraphicsPattern +{ +public : + ImagePattern( const wxBitmap* bmp , const CGAffineTransform& transform ) + { + wxASSERT( bmp && bmp->Ok() ); +#ifdef __WXMAC__ + Init( (CGImageRef) bmp->CreateCGImage() , transform ); +#endif + } + + // ImagePattern takes ownership of CGImageRef passed in + ImagePattern( CGImageRef image , const CGAffineTransform& transform ) + { + if ( image ) + CFRetain( image ); + + Init( image , transform ); + } + + virtual void Render( CGContextRef ctxRef ) + { + if (m_image != NULL) + wxMacDrawCGImage( ctxRef, &m_imageBounds, m_image ); + } + +protected : + void Init( CGImageRef image, const CGAffineTransform& transform ) + { + m_image = image; + if ( m_image ) + { + m_imageBounds = CGRectMake( (CGFloat) 0.0, (CGFloat) 0.0, (CGFloat)CGImageGetWidth( m_image ), (CGFloat)CGImageGetHeight( m_image ) ); + m_patternRef = CGPatternCreate( + this , m_imageBounds, transform , + m_imageBounds.size.width, m_imageBounds.size.height, + kCGPatternTilingNoDistortion, true , &wxMacCoreGraphicsPattern::ms_Callbacks ); + } + } + + virtual ~ImagePattern() + { + if ( m_image ) + CGImageRelease( m_image ); + } + + CGImageRef m_image; + CGRect m_imageBounds; +}; + +class HatchPattern : public wxMacCoreGraphicsPattern +{ +public : + HatchPattern( int hatchstyle, const CGAffineTransform& transform ) + { + m_hatch = hatchstyle; + m_imageBounds = CGRectMake( (CGFloat) 0.0, (CGFloat) 0.0, (CGFloat) 8.0 , (CGFloat) 8.0 ); + m_patternRef = CGPatternCreate( + this , m_imageBounds, transform , + m_imageBounds.size.width, m_imageBounds.size.height, + kCGPatternTilingNoDistortion, false , &wxMacCoreGraphicsPattern::ms_Callbacks ); + } + + void StrokeLineSegments( CGContextRef ctxRef , const CGPoint pts[] , size_t count ) + { + CGContextStrokeLineSegments( ctxRef , pts , count ); + } + + virtual void Render( CGContextRef ctxRef ) + { + switch ( m_hatch ) + { + case wxBDIAGONAL_HATCH : + { + CGPoint pts[] = + { + { (CGFloat) 8.0 , (CGFloat) 0.0 } , { (CGFloat) 0.0 , (CGFloat) 8.0 } + }; + StrokeLineSegments( ctxRef , pts , 2 ); + } + break; + + case wxCROSSDIAG_HATCH : + { + CGPoint pts[] = + { + { (CGFloat) 0.0 , (CGFloat) 0.0 } , { (CGFloat) 8.0 , (CGFloat) 8.0 } , + { (CGFloat) 8.0 , (CGFloat) 0.0 } , { (CGFloat) 0.0 , (CGFloat) 8.0 } + }; + StrokeLineSegments( ctxRef , pts , 4 ); + } + break; + + case wxFDIAGONAL_HATCH : + { + CGPoint pts[] = + { + { (CGFloat) 0.0 , (CGFloat) 0.0 } , { (CGFloat) 8.0 , (CGFloat) 8.0 } + }; + StrokeLineSegments( ctxRef , pts , 2 ); + } + break; + + case wxCROSS_HATCH : + { + CGPoint pts[] = + { + { (CGFloat) 0.0 , (CGFloat) 4.0 } , { (CGFloat) 8.0 , (CGFloat) 4.0 } , + { (CGFloat) 4.0 , (CGFloat) 0.0 } , { (CGFloat) 4.0 , (CGFloat) 8.0 } , + }; + StrokeLineSegments( ctxRef , pts , 4 ); + } + break; + + case wxHORIZONTAL_HATCH : + { + CGPoint pts[] = + { + { (CGFloat) 0.0 , (CGFloat) 4.0 } , { (CGFloat) 8.0 , (CGFloat) 4.0 } , + }; + StrokeLineSegments( ctxRef , pts , 2 ); + } + break; + + case wxVERTICAL_HATCH : + { + CGPoint pts[] = + { + { (CGFloat) 4.0 , (CGFloat) 0.0 } , { (CGFloat) 4.0 , (CGFloat) 8.0 } , + }; + StrokeLineSegments( ctxRef , pts , 2 ); + } + break; + + default: + break; + } + } + +protected : + virtual ~HatchPattern() {} + + CGRect m_imageBounds; + int m_hatch; +}; + +class wxMacCoreGraphicsPenData : public wxGraphicsObjectRefData +{ +public: + wxMacCoreGraphicsPenData( wxGraphicsRenderer* renderer, const wxPen &pen ); + ~wxMacCoreGraphicsPenData(); + + void Init(); + virtual void Apply( wxGraphicsContext* context ); + virtual wxDouble GetWidth() { return m_width; } + +protected : + CGLineCap m_cap; + wxCFRef m_color; + wxCFRef m_colorSpace; + + CGLineJoin m_join; + CGFloat m_width; + + int m_count; + const CGFloat *m_lengths; + CGFloat *m_userLengths; + + + bool m_isPattern; + wxCFRef m_pattern; + CGFloat* m_patternColorComponents; +}; + +wxMacCoreGraphicsPenData::wxMacCoreGraphicsPenData( wxGraphicsRenderer* renderer, const wxPen &pen ) : + wxGraphicsObjectRefData( renderer ) +{ + Init(); + + m_color.reset( wxMacCreateCGColor( pen.GetColour() ) ) ; + + // TODO: * m_dc->m_scaleX + m_width = pen.GetWidth(); + if (m_width <= 0.0) + m_width = (CGFloat) 0.1; + + switch ( pen.GetCap() ) + { + case wxCAP_ROUND : + m_cap = kCGLineCapRound; + break; + + case wxCAP_PROJECTING : + m_cap = kCGLineCapSquare; + break; + + case wxCAP_BUTT : + m_cap = kCGLineCapButt; + break; + + default : + m_cap = kCGLineCapButt; + break; + } + + switch ( pen.GetJoin() ) + { + case wxJOIN_BEVEL : + m_join = kCGLineJoinBevel; + break; + + case wxJOIN_MITER : + m_join = kCGLineJoinMiter; + break; + + case wxJOIN_ROUND : + m_join = kCGLineJoinRound; + break; + + default : + m_join = kCGLineJoinMiter; + break; + } + + const CGFloat dashUnit = m_width < 1.0 ? (CGFloat) 1.0 : m_width; + + const CGFloat dotted[] = { (CGFloat) dashUnit , (CGFloat) (dashUnit + 2.0) }; + static const CGFloat short_dashed[] = { (CGFloat) 9.0 , (CGFloat) 6.0 }; + static const CGFloat dashed[] = { (CGFloat) 19.0 , (CGFloat) 9.0 }; + static const CGFloat dotted_dashed[] = { (CGFloat) 9.0 , (CGFloat) 6.0 , (CGFloat) 3.0 , (CGFloat) 3.0 }; + + switch ( pen.GetStyle() ) + { + case wxSOLID : + break; + + case wxDOT : + m_count = WXSIZEOF(dotted); + m_userLengths = new CGFloat[ m_count ] ; + memcpy( m_userLengths, dotted, sizeof(dotted) ); + m_lengths = m_userLengths; + break; + + case wxLONG_DASH : + m_count = WXSIZEOF(dashed); + m_lengths = dashed; + break; + + case wxSHORT_DASH : + m_count = WXSIZEOF(short_dashed); + m_lengths = short_dashed; + break; + + case wxDOT_DASH : + m_count = WXSIZEOF(dotted_dashed); + m_lengths = dotted_dashed; + break; + + case wxUSER_DASH : + wxDash *dashes; + m_count = pen.GetDashes( &dashes ); + if ((dashes != NULL) && (m_count > 0)) + { + m_userLengths = new CGFloat[m_count]; + for ( int i = 0; i < m_count; ++i ) + { + m_userLengths[i] = dashes[i] * dashUnit; + + if ( i % 2 == 1 && m_userLengths[i] < dashUnit + 2.0 ) + m_userLengths[i] = (CGFloat) (dashUnit + 2.0); + else if ( i % 2 == 0 && m_userLengths[i] < dashUnit ) + m_userLengths[i] = dashUnit; + } + } + m_lengths = m_userLengths; + break; + + case wxSTIPPLE : + { + wxBitmap* bmp = pen.GetStipple(); + if ( bmp && bmp->Ok() ) + { + m_colorSpace.reset( CGColorSpaceCreatePattern( NULL ) ); + m_pattern.reset( (CGPatternRef) *( new ImagePattern( bmp , CGAffineTransformMakeScale( 1,-1 ) ) ) ); + m_patternColorComponents = new CGFloat[1] ; + m_patternColorComponents[0] = (CGFloat) 1.0; + m_isPattern = true; + } + } + break; + + default : + { + m_isPattern = true; + m_colorSpace.reset( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) ); + m_pattern.reset( (CGPatternRef) *( new HatchPattern( pen.GetStyle() , CGAffineTransformMakeScale( 1,-1 ) ) ) ); + m_patternColorComponents = new CGFloat[4] ; + m_patternColorComponents[0] = (CGFloat) (pen.GetColour().Red() / 255.0); + m_patternColorComponents[1] = (CGFloat) (pen.GetColour().Green() / 255.0); + m_patternColorComponents[2] = (CGFloat) (pen.GetColour().Blue() / 255.0); + m_patternColorComponents[3] = (CGFloat) (pen.GetColour().Alpha() / 255.0); + } + break; + } + if ((m_lengths != NULL) && (m_count > 0)) + { + // force the line cap, otherwise we get artifacts (overlaps) and just solid lines + m_cap = kCGLineCapButt; + } +} + +wxMacCoreGraphicsPenData::~wxMacCoreGraphicsPenData() +{ + delete[] m_userLengths; + delete[] m_patternColorComponents; +} + +void wxMacCoreGraphicsPenData::Init() +{ + m_lengths = NULL; + m_userLengths = NULL; + m_width = 0; + m_count = 0; + m_patternColorComponents = NULL; + m_isPattern = false; +} + +void wxMacCoreGraphicsPenData::Apply( wxGraphicsContext* context ) +{ + CGContextRef cg = (CGContextRef) context->GetNativeContext(); + CGContextSetLineWidth( cg , m_width ); + CGContextSetLineJoin( cg , m_join ); + + CGContextSetLineDash( cg , 0 , m_lengths , m_count ); + CGContextSetLineCap( cg , m_cap ); + + if ( m_isPattern ) + { + CGAffineTransform matrix = CGContextGetCTM( cg ); + CGContextSetPatternPhase( cg, CGSizeMake(matrix.tx, matrix.ty) ); + CGContextSetStrokeColorSpace( cg , m_colorSpace ); + CGContextSetStrokePattern( cg, m_pattern , m_patternColorComponents ); + } + else + { + if ( context->GetLogicalFunction() == wxINVERT || context->GetLogicalFunction() == wxXOR ) + { + CGContextSetRGBStrokeColor( cg , (CGFloat) 1.0,(CGFloat) 1.0 , (CGFloat) 1.0, (CGFloat) 1.0 ); + } + else + CGContextSetStrokeColorWithColor( cg , m_color ); + } +} + +// +// Brush +// + +static const char *gs_stripedback_xpm[] = { +/* columns rows colors chars-per-pixel */ +"4 4 2 1", +". c #F0F0F0", +"X c #ECECEC", +/* pixels */ +"....", +"....", +"XXXX", +"XXXX" +}; + +wxBitmap gs_stripedback_bmp( wxImage( (const char* const* ) gs_stripedback_xpm ), -1 ) ; + +// make sure we all use one class for all conversions from wx to native colour + +class wxMacCoreGraphicsColour +{ + public: + wxMacCoreGraphicsColour(); + wxMacCoreGraphicsColour(const wxBrush &brush); + ~wxMacCoreGraphicsColour(); + + void Apply( CGContextRef cgContext ); + protected: + void Init(); + wxCFRef m_color; + wxCFRef m_colorSpace; + + bool m_isPattern; + wxCFRef m_pattern; + CGFloat* m_patternColorComponents; +} ; + +wxMacCoreGraphicsColour::~wxMacCoreGraphicsColour() +{ + delete[] m_patternColorComponents; +} + +void wxMacCoreGraphicsColour::Init() +{ + m_isPattern = false; + m_patternColorComponents = NULL; +} + +void wxMacCoreGraphicsColour::Apply( CGContextRef cgContext ) +{ + if ( m_isPattern ) + { + CGAffineTransform matrix = CGContextGetCTM( cgContext ); + CGContextSetPatternPhase( cgContext, CGSizeMake(matrix.tx, matrix.ty) ); + CGContextSetFillColorSpace( cgContext , m_colorSpace ); + CGContextSetFillPattern( cgContext, m_pattern , m_patternColorComponents ); + } + else + { + CGContextSetFillColorWithColor( cgContext, m_color ); + } +} + +wxMacCoreGraphicsColour::wxMacCoreGraphicsColour() +{ + Init(); +} + +wxMacCoreGraphicsColour::wxMacCoreGraphicsColour( const wxBrush &brush ) +{ + Init(); + if ( brush.GetStyle() == wxSOLID ) + { + m_color.reset( wxMacCreateCGColor( brush.GetColour() )); + } + else if ( brush.IsHatch() ) + { + m_isPattern = true; + m_colorSpace.reset( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) ); + m_pattern.reset( (CGPatternRef) *( new HatchPattern( brush.GetStyle() , CGAffineTransformMakeScale( 1,-1 ) ) ) ); + + m_patternColorComponents = new CGFloat[4] ; + m_patternColorComponents[0] = (CGFloat) (brush.GetColour().Red() / 255.0); + m_patternColorComponents[1] = (CGFloat) (brush.GetColour().Green() / 255.0); + m_patternColorComponents[2] = (CGFloat) (brush.GetColour().Blue() / 255.0); + m_patternColorComponents[3] = (CGFloat) (brush.GetColour().Alpha() / 255.0); + } + else + { + // now brush is a bitmap + wxBitmap* bmp = brush.GetStipple(); + if ( bmp && bmp->Ok() ) + { + m_isPattern = true; + m_patternColorComponents = new CGFloat[1] ; + m_patternColorComponents[0] = (CGFloat) 1.0; + m_colorSpace.reset( CGColorSpaceCreatePattern( NULL ) ); + m_pattern.reset( (CGPatternRef) *( new ImagePattern( bmp , CGAffineTransformMakeScale( 1,-1 ) ) ) ); + } + } +} + +class wxMacCoreGraphicsBrushData : public wxGraphicsObjectRefData +{ +public: + wxMacCoreGraphicsBrushData( wxGraphicsRenderer* renderer ); + wxMacCoreGraphicsBrushData( wxGraphicsRenderer* renderer, const wxBrush &brush ); + ~wxMacCoreGraphicsBrushData (); + + virtual void Apply( wxGraphicsContext* context ); + void CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, + const wxColour&c1, const wxColour&c2 ); + void CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, + const wxColour &oColor, const wxColour &cColor ); + + virtual bool IsShading() { return m_isShading; } + CGShadingRef GetShading() { return m_shading; } +protected: + CGFunctionRef CreateGradientFunction( const wxColour& c1, const wxColour& c2 ); + static void CalculateShadingValues (void *info, const CGFloat *in, CGFloat *out); + virtual void Init(); + + wxMacCoreGraphicsColour m_cgColor; + + bool m_isShading; + CGFunctionRef m_gradientFunction; + CGShadingRef m_shading; + CGFloat *m_gradientComponents; +}; + +wxMacCoreGraphicsBrushData::wxMacCoreGraphicsBrushData( wxGraphicsRenderer* renderer) : wxGraphicsObjectRefData( renderer ) +{ + Init(); +} + +void wxMacCoreGraphicsBrushData::CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, + const wxColour&c1, const wxColour&c2 ) +{ + m_gradientFunction = CreateGradientFunction( c1, c2 ); + m_shading = CGShadingCreateAxial( wxMacGetGenericRGBColorSpace(), CGPointMake((CGFloat) x1, (CGFloat) y1), + CGPointMake((CGFloat) x2,(CGFloat) y2), m_gradientFunction, true, true ) ; + m_isShading = true ; +} + +void wxMacCoreGraphicsBrushData::CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, + const wxColour &oColor, const wxColour &cColor ) +{ + m_gradientFunction = CreateGradientFunction( oColor, cColor ); + m_shading = CGShadingCreateRadial( wxMacGetGenericRGBColorSpace(), CGPointMake((CGFloat) xo,(CGFloat) yo), 0, + CGPointMake((CGFloat) xc,(CGFloat) yc), (CGFloat) radius, m_gradientFunction, true, true ) ; + m_isShading = true ; +} + +wxMacCoreGraphicsBrushData::wxMacCoreGraphicsBrushData(wxGraphicsRenderer* renderer, const wxBrush &brush) : wxGraphicsObjectRefData( renderer ), + m_cgColor( brush ) +{ + Init(); + +} + +wxMacCoreGraphicsBrushData::~wxMacCoreGraphicsBrushData() +{ + if ( m_shading ) + CGShadingRelease(m_shading); + + if( m_gradientFunction ) + CGFunctionRelease(m_gradientFunction); + + delete[] m_gradientComponents; +} + +void wxMacCoreGraphicsBrushData::Init() +{ + m_gradientFunction = NULL; + m_shading = NULL; + m_gradientComponents = NULL; + m_isShading = false; +} + +void wxMacCoreGraphicsBrushData::Apply( wxGraphicsContext* context ) +{ + CGContextRef cg = (CGContextRef) context->GetNativeContext(); + + if ( m_isShading ) + { + // nothing to set as shades are processed by clipping using the path and filling + } + else + { + m_cgColor.Apply( cg ); + } +} + +void wxMacCoreGraphicsBrushData::CalculateShadingValues (void *info, const CGFloat *in, CGFloat *out) +{ + CGFloat* colors = (CGFloat*) info ; + CGFloat f = *in; + for( int i = 0 ; i < 4 ; ++i ) + { + out[i] = colors[i] + ( colors[4+i] - colors[i] ) * f; + } +} + +CGFunctionRef wxMacCoreGraphicsBrushData::CreateGradientFunction( const wxColour& c1, const wxColour& c2 ) +{ + static const CGFunctionCallbacks callbacks = { 0, &CalculateShadingValues, NULL }; + static const CGFloat input_value_range [2] = { 0, 1 }; + static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 }; + m_gradientComponents = new CGFloat[8] ; + m_gradientComponents[0] = (CGFloat) (c1.Red() / 255.0); + m_gradientComponents[1] = (CGFloat) (c1.Green() / 255.0); + m_gradientComponents[2] = (CGFloat) (c1.Blue() / 255.0); + m_gradientComponents[3] = (CGFloat) (c1.Alpha() / 255.0); + m_gradientComponents[4] = (CGFloat) (c2.Red() / 255.0); + m_gradientComponents[5] = (CGFloat) (c2.Green() / 255.0); + m_gradientComponents[6] = (CGFloat) (c2.Blue() / 255.0); + m_gradientComponents[7] = (CGFloat) (c2.Alpha() / 255.0); + + return CGFunctionCreate ( m_gradientComponents, 1, + input_value_range, + 4, + output_value_ranges, + &callbacks); +} + +// +// Font +// + +class wxMacCoreGraphicsFontData : public wxGraphicsObjectRefData +{ +public: + wxMacCoreGraphicsFontData( wxGraphicsRenderer* renderer, const wxFont &font, const wxColour& col ); + ~wxMacCoreGraphicsFontData(); + +#if wxMAC_USE_ATSU_TEXT + virtual ATSUStyle GetATSUStyle() { return m_macATSUIStyle; } +#endif +#if wxMAC_USE_CORE_TEXT + CTFontRef GetCTFont() const { return m_ctFont ; } +#endif + wxColour GetColour() const { return m_colour ; } + + bool GetUnderlined() const { return m_underlined ; } +private : + wxColour m_colour; + bool m_underlined; +#if wxMAC_USE_ATSU_TEXT + ATSUStyle m_macATSUIStyle; +#endif +#if wxMAC_USE_CORE_TEXT + wxCFRef< CTFontRef > m_ctFont; +#endif +}; + +wxMacCoreGraphicsFontData::wxMacCoreGraphicsFontData(wxGraphicsRenderer* renderer, const wxFont &font, const wxColour& col) : wxGraphicsObjectRefData( renderer ) +{ + m_colour = col; + m_underlined = font.GetUnderlined(); + +#if wxMAC_USE_CORE_TEXT + m_ctFont.reset( wxMacCreateCTFont( font ) ); +#endif +#if wxMAC_USE_ATSU_TEXT + OSStatus status = noErr; + m_macATSUIStyle = NULL; + + status = ATSUCreateAndCopyStyle( (ATSUStyle) font.MacGetATSUStyle() , &m_macATSUIStyle ); + + wxASSERT_MSG( status == noErr, wxT("couldn't create ATSU style") ); + + // we need the scale here ... + + Fixed atsuSize = IntToFixed( int( 1 * font.MacGetFontSize()) ); + RGBColor atsuColor ; + col.GetRGBColor( &atsuColor ); + ATSUAttributeTag atsuTags[] = + { + kATSUSizeTag , + kATSUColorTag , + }; + ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + sizeof( Fixed ) , + sizeof( RGBColor ) , + }; + ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + &atsuSize , + &atsuColor , + }; + + status = ::ATSUSetAttributes( + m_macATSUIStyle, sizeof(atsuTags) / sizeof(ATSUAttributeTag) , + atsuTags, atsuSizes, atsuValues); + + wxASSERT_MSG( status == noErr , wxT("couldn't modify ATSU style") ); +#endif +#if wxMAC_USE_CG_TEXT +#endif +} + +wxMacCoreGraphicsFontData::~wxMacCoreGraphicsFontData() +{ +#if wxMAC_USE_CORE_TEXT +#endif +#if wxMAC_USE_ATSU_TEXT + if ( m_macATSUIStyle ) + { + ::ATSUDisposeStyle((ATSUStyle)m_macATSUIStyle); + m_macATSUIStyle = NULL; + } +#endif +#if wxMAC_USE_CG_TEXT +#endif +} + +class wxMacCoreGraphicsBitmapData : public wxGraphicsObjectRefData +{ +public: + wxMacCoreGraphicsBitmapData( wxGraphicsRenderer* renderer, CGImageRef bitmap, bool monochrome ); + ~wxMacCoreGraphicsBitmapData(); + + virtual CGImageRef GetBitmap() { return m_bitmap; } + bool IsMonochrome() { return m_monochrome; } +private : + CGImageRef m_bitmap; + bool m_monochrome; +}; + +wxMacCoreGraphicsBitmapData::wxMacCoreGraphicsBitmapData( wxGraphicsRenderer* renderer, CGImageRef bitmap, bool monochrome ) : wxGraphicsObjectRefData( renderer ), + m_bitmap(bitmap), m_monochrome(monochrome) +{ +} + +wxMacCoreGraphicsBitmapData::~wxMacCoreGraphicsBitmapData() +{ + CGImageRelease( m_bitmap ); +} + +// +// Graphics Matrix +// + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsMatrix declaration +//----------------------------------------------------------------------------- + +class WXDLLIMPEXP_CORE wxMacCoreGraphicsMatrixData : public wxGraphicsMatrixData +{ +public : + wxMacCoreGraphicsMatrixData(wxGraphicsRenderer* renderer) ; + + virtual ~wxMacCoreGraphicsMatrixData() ; + + virtual wxGraphicsObjectRefData *Clone() const ; + + // concatenates the matrix + virtual void Concat( const wxGraphicsMatrixData *t ); + + // sets the matrix to the respective values + virtual void Set(wxDouble a=1.0, wxDouble b=0.0, wxDouble c=0.0, wxDouble d=1.0, + wxDouble tx=0.0, wxDouble ty=0.0); + + // gets the component valuess of the matrix + virtual void Get(wxDouble* a=NULL, wxDouble* b=NULL, wxDouble* c=NULL, + wxDouble* d=NULL, wxDouble* tx=NULL, wxDouble* ty=NULL) const; + + // makes this the inverse matrix + virtual void Invert(); + + // returns true if the elements of the transformation matrix are equal ? + virtual bool IsEqual( const wxGraphicsMatrixData* t) const ; + + // return true if this is the identity matrix + virtual bool IsIdentity() const; + + // + // transformation + // + + // add the translation to this matrix + virtual void Translate( wxDouble dx , wxDouble dy ); + + // add the scale to this matrix + virtual void Scale( wxDouble xScale , wxDouble yScale ); + + // add the rotation to this matrix (radians) + virtual void Rotate( wxDouble angle ); + + // + // apply the transforms + // + + // applies that matrix to the point + virtual void TransformPoint( wxDouble *x, wxDouble *y ) const; + + // applies the matrix except for translations + virtual void TransformDistance( wxDouble *dx, wxDouble *dy ) const; + + // returns the native representation + virtual void * GetNativeMatrix() const; + +private : + CGAffineTransform m_matrix; +} ; + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsMatrix implementation +//----------------------------------------------------------------------------- + +wxMacCoreGraphicsMatrixData::wxMacCoreGraphicsMatrixData(wxGraphicsRenderer* renderer) : wxGraphicsMatrixData(renderer) +{ +} + +wxMacCoreGraphicsMatrixData::~wxMacCoreGraphicsMatrixData() +{ +} + +wxGraphicsObjectRefData *wxMacCoreGraphicsMatrixData::Clone() const +{ + wxMacCoreGraphicsMatrixData* m = new wxMacCoreGraphicsMatrixData(GetRenderer()) ; + m->m_matrix = m_matrix ; + return m; +} + +// concatenates the matrix +void wxMacCoreGraphicsMatrixData::Concat( const wxGraphicsMatrixData *t ) +{ + m_matrix = CGAffineTransformConcat(m_matrix, *((CGAffineTransform*) t->GetNativeMatrix()) ); +} + +// sets the matrix to the respective values +void wxMacCoreGraphicsMatrixData::Set(wxDouble a, wxDouble b, wxDouble c, wxDouble d, + wxDouble tx, wxDouble ty) +{ + m_matrix = CGAffineTransformMake((CGFloat) a,(CGFloat) b,(CGFloat) c,(CGFloat) d,(CGFloat) tx,(CGFloat) ty); +} + +// gets the component valuess of the matrix +void wxMacCoreGraphicsMatrixData::Get(wxDouble* a, wxDouble* b, wxDouble* c, + wxDouble* d, wxDouble* tx, wxDouble* ty) const +{ + if (a) *a = m_matrix.a; + if (b) *b = m_matrix.b; + if (c) *c = m_matrix.c; + if (d) *d = m_matrix.d; + if (tx) *tx= m_matrix.tx; + if (ty) *ty= m_matrix.ty; +} + +// makes this the inverse matrix +void wxMacCoreGraphicsMatrixData::Invert() +{ + m_matrix = CGAffineTransformInvert( m_matrix ); +} + +// returns true if the elements of the transformation matrix are equal ? +bool wxMacCoreGraphicsMatrixData::IsEqual( const wxGraphicsMatrixData* t) const +{ + return CGAffineTransformEqualToTransform(m_matrix, *((CGAffineTransform*) t->GetNativeMatrix())); +} + +// return true if this is the identity matrix +bool wxMacCoreGraphicsMatrixData::IsIdentity() const +{ + return ( m_matrix.a == 1 && m_matrix.d == 1 && + m_matrix.b == 0 && m_matrix.d == 0 && m_matrix.tx == 0 && m_matrix.ty == 0); +} + +// +// transformation +// + +// add the translation to this matrix +void wxMacCoreGraphicsMatrixData::Translate( wxDouble dx , wxDouble dy ) +{ + m_matrix = CGAffineTransformTranslate( m_matrix, (CGFloat) dx, (CGFloat) dy); +} + +// add the scale to this matrix +void wxMacCoreGraphicsMatrixData::Scale( wxDouble xScale , wxDouble yScale ) +{ + m_matrix = CGAffineTransformScale( m_matrix, (CGFloat) xScale, (CGFloat) yScale); +} + +// add the rotation to this matrix (radians) +void wxMacCoreGraphicsMatrixData::Rotate( wxDouble angle ) +{ + m_matrix = CGAffineTransformRotate( m_matrix, (CGFloat) angle); +} + +// +// apply the transforms +// + +// applies that matrix to the point +void wxMacCoreGraphicsMatrixData::TransformPoint( wxDouble *x, wxDouble *y ) const +{ + CGPoint pt = CGPointApplyAffineTransform( CGPointMake((CGFloat) *x,(CGFloat) *y), m_matrix); + + *x = pt.x; + *y = pt.y; +} + +// applies the matrix except for translations +void wxMacCoreGraphicsMatrixData::TransformDistance( wxDouble *dx, wxDouble *dy ) const +{ + CGSize sz = CGSizeApplyAffineTransform( CGSizeMake((CGFloat) *dx,(CGFloat) *dy) , m_matrix ); + *dx = sz.width; + *dy = sz.height; +} + +// returns the native representation +void * wxMacCoreGraphicsMatrixData::GetNativeMatrix() const +{ + return (void*) &m_matrix; +} + +// +// Graphics Path +// + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsPath declaration +//----------------------------------------------------------------------------- + +class WXDLLEXPORT wxMacCoreGraphicsPathData : public wxGraphicsPathData +{ +public : + wxMacCoreGraphicsPathData( wxGraphicsRenderer* renderer, CGMutablePathRef path = NULL); + + ~wxMacCoreGraphicsPathData(); + + virtual wxGraphicsObjectRefData *Clone() const; + + // begins a new subpath at (x,y) + virtual void MoveToPoint( wxDouble x, wxDouble y ); + + // adds a straight line from the current point to (x,y) + virtual void AddLineToPoint( wxDouble x, wxDouble y ); + + // adds a cubic Bezier curve from the current point, using two control points and an end point + virtual void AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y ); + + // closes the current sub-path + virtual void CloseSubpath(); + + // gets the last point of the current path, (0,0) if not yet set + virtual void GetCurrentPoint( wxDouble* x, wxDouble* y) const; + + // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle + virtual void AddArc( wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise ); + + // + // These are convenience functions which - if not available natively will be assembled + // using the primitives from above + // + + // adds a quadratic Bezier curve from the current point, using a control point and an end point + virtual void AddQuadCurveToPoint( wxDouble cx, wxDouble cy, wxDouble x, wxDouble y ); + + // appends a rectangle as a new closed subpath + virtual void AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + + // appends an ellipsis as a new closed subpath fitting the passed rectangle + virtual void AddCircle( wxDouble x, wxDouble y, wxDouble r ); + + // draws a an arc to two tangents connecting (current) to (x1,y1) and (x1,y1) to (x2,y2), also a straight line from (current) to (x1,y1) + virtual void AddArcToPoint( wxDouble x1, wxDouble y1 , wxDouble x2, wxDouble y2, wxDouble r ); + + // adds another path + virtual void AddPath( const wxGraphicsPathData* path ); + + // returns the native path + virtual void * GetNativePath() const { return m_path; } + + // give the native path returned by GetNativePath() back (there might be some deallocations necessary) + virtual void UnGetNativePath(void *WXUNUSED(p)) const {} + + // transforms each point of this path by the matrix + virtual void Transform( const wxGraphicsMatrixData* matrix ); + + // gets the bounding box enclosing all points (possibly including control points) + virtual void GetBox(wxDouble *x, wxDouble *y, wxDouble *w, wxDouble *y) const; + + virtual bool Contains( wxDouble x, wxDouble y, int fillStyle = wxODDEVEN_RULE) const; +private : + CGMutablePathRef m_path; +}; + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsPath implementation +//----------------------------------------------------------------------------- + +wxMacCoreGraphicsPathData::wxMacCoreGraphicsPathData( wxGraphicsRenderer* renderer, CGMutablePathRef path) : wxGraphicsPathData(renderer) +{ + if ( path ) + m_path = path; + else + m_path = CGPathCreateMutable(); +} + +wxMacCoreGraphicsPathData::~wxMacCoreGraphicsPathData() +{ + CGPathRelease( m_path ); +} + +wxGraphicsObjectRefData* wxMacCoreGraphicsPathData::Clone() const +{ + wxMacCoreGraphicsPathData* clone = new wxMacCoreGraphicsPathData(GetRenderer(),CGPathCreateMutableCopy(m_path)); + return clone ; +} + + +// opens (starts) a new subpath +void wxMacCoreGraphicsPathData::MoveToPoint( wxDouble x1 , wxDouble y1 ) +{ + CGPathMoveToPoint( m_path , NULL , (CGFloat) x1 , (CGFloat) y1 ); +} + +void wxMacCoreGraphicsPathData::AddLineToPoint( wxDouble x1 , wxDouble y1 ) +{ + CGPathAddLineToPoint( m_path , NULL , (CGFloat) x1 , (CGFloat) y1 ); +} + +void wxMacCoreGraphicsPathData::AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y ) +{ + CGPathAddCurveToPoint( m_path , NULL , (CGFloat) cx1 , (CGFloat) cy1 , (CGFloat) cx2, (CGFloat) cy2, (CGFloat) x , (CGFloat) y ); +} + +void wxMacCoreGraphicsPathData::AddQuadCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble x, wxDouble y ) +{ + CGPathAddQuadCurveToPoint( m_path , NULL , (CGFloat) cx1 , (CGFloat) cy1 , (CGFloat) x , (CGFloat) y ); +} + +void wxMacCoreGraphicsPathData::AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h ) +{ + CGRect cgRect = { { (CGFloat) x , (CGFloat) y } , { (CGFloat) w , (CGFloat) h } }; + CGPathAddRect( m_path , NULL , cgRect ); +} + +void wxMacCoreGraphicsPathData::AddCircle( wxDouble x, wxDouble y , wxDouble r ) +{ + CGPathAddArc( m_path , NULL , (CGFloat) x , (CGFloat) y , (CGFloat) r , (CGFloat) 0.0 , (CGFloat) (2 * M_PI) , true ); +} + +// adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle +void wxMacCoreGraphicsPathData::AddArc( wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise ) +{ + // inverse direction as we the 'normal' state is a y axis pointing down, ie mirrored to the standard core graphics setup + CGPathAddArc( m_path, NULL , (CGFloat) x, (CGFloat) y, (CGFloat) r, (CGFloat) startAngle, (CGFloat) endAngle, !clockwise); +} + +void wxMacCoreGraphicsPathData::AddArcToPoint( wxDouble x1, wxDouble y1 , wxDouble x2, wxDouble y2, wxDouble r ) +{ + CGPathAddArcToPoint( m_path, NULL , (CGFloat) x1, (CGFloat) y1, (CGFloat) x2, (CGFloat) y2, (CGFloat) r); +} + +void wxMacCoreGraphicsPathData::AddPath( const wxGraphicsPathData* path ) +{ + CGPathAddPath( m_path , NULL, (CGPathRef) path->GetNativePath() ); +} + +// closes the current subpath +void wxMacCoreGraphicsPathData::CloseSubpath() +{ + CGPathCloseSubpath( m_path ); +} + +// gets the last point of the current path, (0,0) if not yet set +void wxMacCoreGraphicsPathData::GetCurrentPoint( wxDouble* x, wxDouble* y) const +{ + CGPoint p = CGPathGetCurrentPoint( m_path ); + *x = p.x; + *y = p.y; +} + +// transforms each point of this path by the matrix +void wxMacCoreGraphicsPathData::Transform( const wxGraphicsMatrixData* matrix ) +{ + CGMutablePathRef p = CGPathCreateMutable() ; + CGPathAddPath( p, (CGAffineTransform*) matrix->GetNativeMatrix() , m_path ); + CGPathRelease( m_path ); + m_path = p; +} + +// gets the bounding box enclosing all points (possibly including control points) +void wxMacCoreGraphicsPathData::GetBox(wxDouble *x, wxDouble *y, wxDouble *w, wxDouble *h) const +{ + CGRect bounds = CGPathGetBoundingBox( m_path ) ; + *x = bounds.origin.x; + *y = bounds.origin.y; + *w = bounds.size.width; + *h = bounds.size.height; +} + +bool wxMacCoreGraphicsPathData::Contains( wxDouble x, wxDouble y, int fillStyle) const +{ + return CGPathContainsPoint( m_path, NULL, CGPointMake((CGFloat) x,(CGFloat) y), fillStyle == wxODDEVEN_RULE ); +} + +// +// Graphics Context +// + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsContext declaration +//----------------------------------------------------------------------------- + +class WXDLLEXPORT wxMacCoreGraphicsContext : public wxGraphicsContext +{ +public: + wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer, CGContextRef cgcontext, wxDouble width = 0, wxDouble height = 0 ); + + wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer, WindowRef window ); + + wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer, wxWindow* window ); + + wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer); + + wxMacCoreGraphicsContext(); + + ~wxMacCoreGraphicsContext(); + + void Init(); + + // returns the size of the graphics context in device coordinates + virtual void GetSize( wxDouble* width, wxDouble* height); + + virtual void StartPage( wxDouble width, wxDouble height ); + + virtual void EndPage(); + + virtual void Flush(); + + // push the current state of the context, ie the transformation matrix on a stack + virtual void PushState(); + + // pops a stored state from the stack + virtual void PopState(); + + // clips drawings to the region + virtual void Clip( const wxRegion ®ion ); + + // clips drawings to the rect + virtual void Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + + // resets the clipping to original extent + virtual void ResetClip(); + + virtual void * GetNativeContext(); + + bool SetLogicalFunction( int function ); + // + // transformation + // + + // translate + virtual void Translate( wxDouble dx , wxDouble dy ); + + // scale + virtual void Scale( wxDouble xScale , wxDouble yScale ); + + // rotate (radians) + virtual void Rotate( wxDouble angle ); + + // concatenates this transform with the current transform of this context + virtual void ConcatTransform( const wxGraphicsMatrix& matrix ); + + // sets the transform of this context + virtual void SetTransform( const wxGraphicsMatrix& matrix ); + + // gets the matrix of this context + virtual wxGraphicsMatrix GetTransform() const; + // + // setting the paint + // + + // strokes along a path with the current pen + virtual void StrokePath( const wxGraphicsPath &path ); + + // fills a path with the current brush + virtual void FillPath( const wxGraphicsPath &path, int fillStyle = wxODDEVEN_RULE ); + + // draws a path by first filling and then stroking + virtual void DrawPath( const wxGraphicsPath &path, int fillStyle = wxODDEVEN_RULE ); + + virtual bool ShouldOffset() const + { + int penwidth = 0 ; + if ( !m_pen.IsNull() ) + { + penwidth = (int)((wxMacCoreGraphicsPenData*)m_pen.GetRefData())->GetWidth(); + if ( penwidth == 0 ) + penwidth = 1; + } + return ( penwidth % 2 ) == 1; + } + // + // text + // + + virtual void DrawText( const wxString &str, wxDouble x, wxDouble y ); + + virtual void DrawText( const wxString &str, wxDouble x, wxDouble y, wxDouble angle ); + + virtual void GetTextExtent( const wxString &text, wxDouble *width, wxDouble *height, + wxDouble *descent, wxDouble *externalLeading ) const; + + virtual void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const; + + // + // image support + // + + virtual void DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + + virtual void DrawBitmap( const wxGraphicsBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + + virtual void DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h ); + + void SetNativeContext( CGContextRef cg ); + + DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext) + DECLARE_DYNAMIC_CLASS(wxMacCoreGraphicsContext) + +private: + void EnsureIsValid(); + + CGContextRef m_cgContext; + WindowRef m_windowRef; + bool m_releaseContext; + CGAffineTransform m_windowTransform; + wxDouble m_width; + wxDouble m_height; + + wxCFRef m_clipRgn; +}; + +//----------------------------------------------------------------------------- +// device context implementation +// +// more and more of the dc functionality should be implemented by calling +// the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step +// also coordinate conversions should be moved to native matrix ops +//----------------------------------------------------------------------------- + +// we always stock two context states, one at entry, to be able to preserve the +// state we were called with, the other one after changing to HI Graphics orientation +// (this one is used for getting back clippings etc) + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsContext implementation +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsContext, wxGraphicsContext) + +class wxQuartzOffsetHelper +{ +public : + wxQuartzOffsetHelper( CGContextRef cg , bool offset ) + { + m_cg = cg; + m_offset = offset; + if ( m_offset ) + CGContextTranslateCTM( m_cg, (CGFloat) 0.5, (CGFloat) 0.5 ); + } + ~wxQuartzOffsetHelper( ) + { + if ( m_offset ) + CGContextTranslateCTM( m_cg, (CGFloat) -0.5, (CGFloat) -0.5 ); + } +public : + CGContextRef m_cg; + bool m_offset; +} ; + +void wxMacCoreGraphicsContext::Init() +{ + m_cgContext = NULL; + m_releaseContext = false; + m_windowRef = NULL; + m_width = 0; + m_height = 0; +} + +wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer, CGContextRef cgcontext, wxDouble width, wxDouble height ) : wxGraphicsContext(renderer) +{ + Init(); + SetNativeContext(cgcontext); + m_width = width; + m_height = height; +} + +wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer, WindowRef window ): wxGraphicsContext(renderer) +{ + Init(); + m_windowRef = window; +} + +wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( wxGraphicsRenderer* renderer, wxWindow* window ): wxGraphicsContext(renderer) +{ + Init(); + + int originX , originY; + originX = originY = 0; + + Rect bounds = { 0,0,0,0 }; +#if defined( __LP64__ ) || defined(__WXCOCOA__) +#else + m_windowRef = (WindowRef) window->MacGetTopLevelWindowRef(); + window->MacWindowToRootWindow( &originX , &originY ); + GetWindowBounds( m_windowRef, kWindowContentRgn, &bounds ); +#endif + m_windowTransform = CGAffineTransformMakeTranslation( 0 , bounds.bottom - bounds.top ); + m_windowTransform = CGAffineTransformScale( m_windowTransform , 1 , -1 ); + m_windowTransform = CGAffineTransformTranslate( m_windowTransform, originX, originY ) ; +} + +wxMacCoreGraphicsContext::wxMacCoreGraphicsContext(wxGraphicsRenderer* renderer) : wxGraphicsContext(renderer) +{ + Init(); +} + +wxMacCoreGraphicsContext::wxMacCoreGraphicsContext() : wxGraphicsContext(NULL) +{ + Init(); + wxLogDebug(wxT("Illegal Constructor called")); +} + +wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext() +{ + SetNativeContext(NULL); +} + +void wxMacCoreGraphicsContext::GetSize( wxDouble* width, wxDouble* height) +{ + *width = m_width; + *height = m_height; +} + + +void wxMacCoreGraphicsContext::StartPage( wxDouble width, wxDouble height ) +{ + CGRect r; + if ( width != 0 && height != 0) + r = CGRectMake( (CGFloat) 0.0 , (CGFloat) 0.0 , (CGFloat) width , (CGFloat) height ); + else + r = CGRectMake( (CGFloat) 0.0 , (CGFloat) 0.0 , (CGFloat) m_width , (CGFloat) m_height ); + + CGContextBeginPage(m_cgContext, &r ); +// CGContextTranslateCTM( m_cgContext , 0 , height == 0 ? m_height : height ); +// CGContextScaleCTM( m_cgContext , 1 , -1 ); +} + +void wxMacCoreGraphicsContext::EndPage() +{ + CGContextEndPage(m_cgContext); +} + +void wxMacCoreGraphicsContext::Flush() +{ + CGContextFlush(m_cgContext); +} + +void wxMacCoreGraphicsContext::EnsureIsValid() +{ + if ( !m_cgContext ) + { + OSStatus status = +#if ! ( defined( __LP64__ ) || defined(__WXCOCOA__) ) + QDBeginCGContext( GetWindowPort( m_windowRef ) , &m_cgContext ); +#else + paramErr; +#endif + if ( status != noErr ) + { + wxFAIL_MSG("Cannot nest wxDCs on the same window"); + } + + CGContextConcatCTM( m_cgContext, m_windowTransform ); + CGContextSaveGState( m_cgContext ); + m_releaseContext = true; + if ( m_clipRgn.get() ) + { + // the clip region is in device coordinates, so we convert this again to user coordinates + wxCFRef hishape( HIShapeCreateMutableCopy( m_clipRgn ) ); + CGPoint transformedOrigin = CGPointApplyAffineTransform( CGPointZero,m_windowTransform); + HIShapeOffset( hishape, -transformedOrigin.x, -transformedOrigin.y ); + // if the shape is empty, HIShapeReplacePathInCGContext doesn't work + if ( HIShapeIsEmpty(hishape)) + { + CGRect empty = CGRectMake( 0,0,0,0 ); + CGContextClipToRect( m_cgContext, empty ); + } + else + { + HIShapeReplacePathInCGContext( hishape, m_cgContext ); + CGContextClip( m_cgContext ); + } + } + CGContextSaveGState( m_cgContext ); + } +} + +// TODO test whether the private CGContextSetCompositeOperation works under 10.3 (using NSCompositingModes) + +bool wxMacCoreGraphicsContext::SetLogicalFunction( int function ) +{ + if (m_logicalFunction == function) + return true; + + EnsureIsValid(); + + bool retval = false; + bool shouldAntiAlias = true; + CGBlendMode mode = kCGBlendModeNormal; + +#if defined(__WXMAC__) && ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 ) + if ( UMAGetSystemVersion() >= 0x1050 ) + { + retval = true; + switch ( function ) + { + // TODO find best corresponding porter duff modes + case wxCOPY : + mode = kCGBlendModeCopy; + break; + case wxCLEAR : + mode = kCGBlendModeClear; + break; + case wxXOR : + mode = kCGBlendModeXOR; + shouldAntiAlias = false; + break; + default : + retval = false; + break; + } + } + else +#endif + { + if ( function == wxCOPY ) + { + retval = true; + } + else if ( function == wxINVERT || function == wxXOR ) + { + // change color to white + mode = kCGBlendModeExclusion; + shouldAntiAlias = false; + retval = true; + } + } + + if (retval) + { + m_logicalFunction = function; + CGContextSetBlendMode( m_cgContext, mode ); + CGContextSetShouldAntialias(m_cgContext, shouldAntiAlias); + } + return retval ; +} + +void wxMacCoreGraphicsContext::Clip( const wxRegion ®ion ) +{ +#ifdef __WXMAC__ + if( m_cgContext ) + { + wxCFRef shape = wxCFRefFromGet(region.GetWXHRGN()); + // if the shape is empty, HIShapeReplacePathInCGContext doesn't work + if ( HIShapeIsEmpty(shape)) + { + CGRect empty = CGRectMake( 0,0,0,0 ); + CGContextClipToRect( m_cgContext, empty ); + } + else + { + HIShapeReplacePathInCGContext( shape, m_cgContext ); + CGContextClip( m_cgContext ); + } + } + else + { + // this offsetting to device coords is not really correct, but since we cannot apply affine transforms + // to regions we try at least to have correct translations + HIMutableShapeRef mutableShape = HIShapeCreateMutableCopy( region.GetWXHRGN() ); + + CGPoint transformedOrigin = CGPointApplyAffineTransform( CGPointZero, m_windowTransform ); + HIShapeOffset( mutableShape, transformedOrigin.x, transformedOrigin.y ); + m_clipRgn.reset(mutableShape); + } +#endif +} + +// clips drawings to the rect +void wxMacCoreGraphicsContext::Clip( wxDouble x, wxDouble y, wxDouble w, wxDouble h ) +{ + CGRect r = CGRectMake( (CGFloat) x , (CGFloat) y , (CGFloat) w , (CGFloat) h ); + if ( m_cgContext ) + { + CGContextClipToRect( m_cgContext, r ); + } + else + { + // the clipping itself must be stored as device coordinates, otherwise + // we cannot apply it back correctly + r.origin= CGPointApplyAffineTransform( r.origin, m_windowTransform ); + m_clipRgn.reset(HIShapeCreateWithRect(&r)); + } +} + + // resets the clipping to original extent +void wxMacCoreGraphicsContext::ResetClip() +{ + if ( m_cgContext ) + { + // there is no way for clearing the clip, we can only revert to the stored + // state, but then we have to make sure everything else is NOT restored + CGAffineTransform transform = CGContextGetCTM( m_cgContext ); + CGContextRestoreGState( m_cgContext ); + CGContextSaveGState( m_cgContext ); + CGAffineTransform transformNew = CGContextGetCTM( m_cgContext ); + transformNew = CGAffineTransformInvert( transformNew ) ; + CGContextConcatCTM( m_cgContext, transformNew); + CGContextConcatCTM( m_cgContext, transform); + } + else + { + m_clipRgn.reset(); + } +} + +void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath &path ) +{ + if ( m_pen.IsNull() ) + return ; + + EnsureIsValid(); + + wxQuartzOffsetHelper helper( m_cgContext , ShouldOffset() ); + + ((wxMacCoreGraphicsPenData*)m_pen.GetRefData())->Apply(this); + CGContextAddPath( m_cgContext , (CGPathRef) path.GetNativePath() ); + CGContextStrokePath( m_cgContext ); +} + +void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath &path , int fillStyle ) +{ + if ( !m_brush.IsNull() && ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->IsShading() ) + { + // when using shading, we cannot draw pen and brush at the same time + // revert to the base implementation of first filling and then stroking + wxGraphicsContext::DrawPath( path, fillStyle ); + return; + } + + CGPathDrawingMode mode = kCGPathFill ; + if ( m_brush.IsNull() ) + { + if ( m_pen.IsNull() ) + return; + else + mode = kCGPathStroke; + } + else + { + if ( m_pen.IsNull() ) + { + if ( fillStyle == wxODDEVEN_RULE ) + mode = kCGPathEOFill; + else + mode = kCGPathFill; + } + else + { + if ( fillStyle == wxODDEVEN_RULE ) + mode = kCGPathEOFillStroke; + else + mode = kCGPathFillStroke; + } + } + + EnsureIsValid(); + + if ( !m_brush.IsNull() ) + ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->Apply(this); + if ( !m_pen.IsNull() ) + ((wxMacCoreGraphicsPenData*)m_pen.GetRefData())->Apply(this); + + wxQuartzOffsetHelper helper( m_cgContext , ShouldOffset() ); + + CGContextAddPath( m_cgContext , (CGPathRef) path.GetNativePath() ); + CGContextDrawPath( m_cgContext , mode ); +} + +void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath &path , int fillStyle ) +{ + if ( m_brush.IsNull() ) + return; + + EnsureIsValid(); + + if ( ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->IsShading() ) + { + CGContextSaveGState( m_cgContext ); + CGContextAddPath( m_cgContext , (CGPathRef) path.GetNativePath() ); + CGContextClip( m_cgContext ); + CGContextDrawShading( m_cgContext, ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->GetShading() ); + CGContextRestoreGState( m_cgContext); + } + else + { + ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->Apply(this); + CGContextAddPath( m_cgContext , (CGPathRef) path.GetNativePath() ); + if ( fillStyle == wxODDEVEN_RULE ) + CGContextEOFillPath( m_cgContext ); + else + CGContextFillPath( m_cgContext ); + } +} + +void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg ) +{ + // we allow either setting or clearing but not replacing + wxASSERT( m_cgContext == NULL || cg == NULL ); + + if ( m_cgContext ) + { + // TODO : when is this necessary - should we add a Flush() method ? CGContextSynchronize( m_cgContext ); + CGContextRestoreGState( m_cgContext ); + CGContextRestoreGState( m_cgContext ); + if ( m_releaseContext ) + { +#if ! ( defined( __LP64__ ) || defined(__WXCOCOA__) ) + QDEndCGContext( GetWindowPort( m_windowRef ) , &m_cgContext); +#endif + } + else + CGContextRelease(m_cgContext); + } + + + m_cgContext = cg; + + // FIXME: This check is needed because currently we need to use a DC/GraphicsContext + // in order to get font properties, like wxFont::GetPixelSize, but since we don't have + // a native window attached to use, I create a wxGraphicsContext with a NULL CGContextRef + // for this one operation. + + // When wxFont::GetPixelSize on Mac no longer needs a graphics context, this check + // can be removed. + if (m_cgContext) + { + CGContextRetain(m_cgContext); + CGContextSaveGState( m_cgContext ); + CGContextSetTextMatrix( m_cgContext, CGAffineTransformIdentity ); + CGContextSaveGState( m_cgContext ); + m_releaseContext = false; + } +} + +void wxMacCoreGraphicsContext::Translate( wxDouble dx , wxDouble dy ) +{ + if ( m_cgContext ) + CGContextTranslateCTM( m_cgContext, (CGFloat) dx, (CGFloat) dy ); + else + m_windowTransform = CGAffineTransformTranslate(m_windowTransform, (CGFloat) dx, (CGFloat) dy); +} + +void wxMacCoreGraphicsContext::Scale( wxDouble xScale , wxDouble yScale ) +{ + if ( m_cgContext ) + CGContextScaleCTM( m_cgContext , (CGFloat) xScale , (CGFloat) yScale ); + else + m_windowTransform = CGAffineTransformScale(m_windowTransform, (CGFloat) xScale, (CGFloat) yScale); +} + +void wxMacCoreGraphicsContext::Rotate( wxDouble angle ) +{ + if ( m_cgContext ) + CGContextRotateCTM( m_cgContext , (CGFloat) angle ); + else + m_windowTransform = CGAffineTransformRotate(m_windowTransform, (CGFloat) angle); +} + +void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h ) +{ + wxGraphicsBitmap bitmap = GetRenderer()->CreateBitmap(bmp); + DrawBitmap(bitmap, x, y, w, h); +} + +void wxMacCoreGraphicsContext::DrawBitmap( const wxGraphicsBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h ) +{ + EnsureIsValid(); +#ifdef __WXMAC__ + wxMacCoreGraphicsBitmapData* refdata =static_cast(bmp.GetRefData()); + CGImageRef image = refdata->GetBitmap(); + CGRect r = CGRectMake( (CGFloat) x , (CGFloat) y , (CGFloat) w , (CGFloat) h ); + if ( refdata->IsMonochrome() == 1 ) + { + // is is a mask, the '1' in the mask tell where to draw the current brush + if ( !m_brush.IsNull() ) + { + if ( ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->IsShading() ) + { + // TODO clip to mask + /* + CGContextSaveGState( m_cgContext ); + CGContextAddPath( m_cgContext , (CGPathRef) path.GetNativePath() ); + CGContextClip( m_cgContext ); + CGContextDrawShading( m_cgContext, ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->GetShading() ); + CGContextRestoreGState( m_cgContext); + */ + } + else + { + ((wxMacCoreGraphicsBrushData*)m_brush.GetRefData())->Apply(this); + wxMacDrawCGImage( m_cgContext , &r , image ); + } + } + } + else + { + wxMacDrawCGImage( m_cgContext , &r , image ); + } +#endif +} + +void wxMacCoreGraphicsContext::DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h ) +{ + EnsureIsValid(); + + CGRect r = CGRectMake( (CGFloat) 0.0 , (CGFloat) 0.0 , (CGFloat) w , (CGFloat) h ); + CGContextSaveGState( m_cgContext ); + CGContextTranslateCTM( m_cgContext,(CGFloat) x ,(CGFloat) (y + h) ); + CGContextScaleCTM( m_cgContext, 1, -1 ); +#ifdef __WXMAC__ + PlotIconRefInContext( m_cgContext , &r , kAlignNone , kTransformNone , + NULL , kPlotIconRefNormalFlags , MAC_WXHICON( icon.GetHICON() ) ); +#endif + CGContextRestoreGState( m_cgContext ); +} + +void wxMacCoreGraphicsContext::PushState() +{ + EnsureIsValid(); + + CGContextSaveGState( m_cgContext ); +} + +void wxMacCoreGraphicsContext::PopState() +{ + EnsureIsValid(); + + CGContextRestoreGState( m_cgContext ); +} + +void wxMacCoreGraphicsContext::DrawText( const wxString &str, wxDouble x, wxDouble y ) +{ + if ( m_font.IsNull() ) + return; + + EnsureIsValid(); +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); + wxCFStringRef text(str, wxLocale::GetSystemEncoding() ); + CTFontRef font = fref->GetCTFont(); + CGColorRef col = wxMacCreateCGColor( fref->GetColour() ); + CTUnderlineStyle ustyle = fref->GetUnderlined() ? kCTUnderlineStyleSingle : kCTUnderlineStyleNone ; + wxCFRef underlined( CFNumberCreate(NULL, kCFNumberSInt32Type, &ustyle) ); + CFStringRef keys[] = { kCTFontAttributeName , kCTForegroundColorAttributeName, kCTUnderlineStyleAttributeName }; + CFTypeRef values[] = { font, col, underlined }; + wxCFRef attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values, + WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) ); + wxCFRef attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, attributes) ); + wxCFRef line( CTLineCreateWithAttributedString(attrtext) ); + + y += CTFontGetAscent(font); + + CGContextSaveGState(m_cgContext); + CGContextTranslateCTM(m_cgContext, x, y); + CGContextScaleCTM(m_cgContext, 1, -1); + CGContextSetTextPosition(m_cgContext, 0, 0); + CTLineDraw( line, m_cgContext ); + CGContextRestoreGState(m_cgContext); + CFRelease( col ); + return; + } +#endif +#if wxMAC_USE_ATSU_TEXT + { + DrawText(str, x, y, 0.0); + return; + } +#endif +#if wxMAC_USE_CG_TEXT + // TODO core graphics text implementation here +#endif +} + +void wxMacCoreGraphicsContext::DrawText( const wxString &str, wxDouble x, wxDouble y, wxDouble angle ) +{ + if ( m_font.IsNull() ) + return; + + EnsureIsValid(); +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + // default implementation takes care of rotation and calls non rotated DrawText afterwards + wxGraphicsContext::DrawText( str, x, y, angle ); + return; + } +#endif +#if wxMAC_USE_ATSU_TEXT + { + OSStatus status = noErr; + ATSUTextLayout atsuLayout; + wxMacUniCharBuffer unibuf( str ); + UniCharCount chars = unibuf.GetChars(); + + ATSUStyle style = (((wxMacCoreGraphicsFontData*)m_font.GetRefData())->GetATSUStyle()); + status = ::ATSUCreateTextLayoutWithTextPtr( unibuf.GetBuffer() , 0 , chars , chars , 1 , + &chars , &style , &atsuLayout ); + + wxASSERT_MSG( status == noErr , wxT("couldn't create the layout of the rotated text") ); + + status = ::ATSUSetTransientFontMatching( atsuLayout , true ); + wxASSERT_MSG( status == noErr , wxT("couldn't setup transient font matching") ); + + int iAngle = int( angle * RAD2DEG ); + if ( abs(iAngle) > 0 ) + { + Fixed atsuAngle = IntToFixed( iAngle ); + ATSUAttributeTag atsuTags[] = + { + kATSULineRotationTag , + }; + ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + sizeof( Fixed ) , + }; + ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + &atsuAngle , + }; + status = ::ATSUSetLayoutControls(atsuLayout , sizeof(atsuTags) / sizeof(ATSUAttributeTag), + atsuTags, atsuSizes, atsuValues ); + } + + { + ATSUAttributeTag atsuTags[] = + { + kATSUCGContextTag , + }; + ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + sizeof( CGContextRef ) , + }; + ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] = + { + &m_cgContext , + }; + status = ::ATSUSetLayoutControls(atsuLayout , sizeof(atsuTags) / sizeof(ATSUAttributeTag), + atsuTags, atsuSizes, atsuValues ); + } + + ATSUTextMeasurement textBefore, textAfter; + ATSUTextMeasurement ascent, descent; + + status = ::ATSUGetUnjustifiedBounds( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd, + &textBefore , &textAfter, &ascent , &descent ); + + wxASSERT_MSG( status == noErr , wxT("couldn't measure the rotated text") ); + + Rect rect; + x += (int)(sin(angle) * FixedToInt(ascent)); + y += (int)(cos(angle) * FixedToInt(ascent)); + + status = ::ATSUMeasureTextImage( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd, + IntToFixed(x) , IntToFixed(y) , &rect ); + wxASSERT_MSG( status == noErr , wxT("couldn't measure the rotated text") ); + + CGContextSaveGState(m_cgContext); + CGContextTranslateCTM(m_cgContext, (CGFloat) x, (CGFloat) y); + CGContextScaleCTM(m_cgContext, 1, -1); + status = ::ATSUDrawText( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd, + IntToFixed(0) , IntToFixed(0) ); + + wxASSERT_MSG( status == noErr , wxT("couldn't draw the rotated text") ); + + CGContextRestoreGState(m_cgContext); + + ::ATSUDisposeTextLayout(atsuLayout); + + return; + } +#endif +#if wxMAC_USE_CG_TEXT + // default implementation takes care of rotation and calls non rotated DrawText afterwards + wxGraphicsContext::DrawText( str, x, y, angle ); +#endif +} + +void wxMacCoreGraphicsContext::GetTextExtent( const wxString &str, wxDouble *width, wxDouble *height, + wxDouble *descent, wxDouble *externalLeading ) const +{ + wxCHECK_RET( !m_font.IsNull(), wxT("wxDC(cg)::DoGetTextExtent - no valid font set") ); + + if ( width ) + *width = 0; + if ( height ) + *height = 0; + if ( descent ) + *descent = 0; + if ( externalLeading ) + *externalLeading = 0; + + if (str.empty()) + return; + +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); + CTFontRef font = fref->GetCTFont(); + + wxCFStringRef text(str, wxLocale::GetSystemEncoding() ); + CFStringRef keys[] = { kCTFontAttributeName }; + CFTypeRef values[] = { font }; + wxCFRef attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values, + WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) ); + wxCFRef attrtext( CFAttributedStringCreate(kCFAllocatorDefault, text, attributes) ); + wxCFRef line( CTLineCreateWithAttributedString(attrtext) ); + + CGFloat w, a, d, l; + + w = CTLineGetTypographicBounds(line, &a, &d, &l) ; + + if ( height ) + *height = a+d+l; + if ( descent ) + *descent = d; + if ( externalLeading ) + *externalLeading = l; + if ( width ) + *width = w; + return; + } +#endif +#if wxMAC_USE_ATSU_TEXT + { + OSStatus status = noErr; + + ATSUTextLayout atsuLayout; + wxMacUniCharBuffer unibuf( str ); + UniCharCount chars = unibuf.GetChars(); + + ATSUStyle style = (((wxMacCoreGraphicsFontData*)m_font.GetRefData())->GetATSUStyle()); + status = ::ATSUCreateTextLayoutWithTextPtr( unibuf.GetBuffer() , 0 , chars , chars , 1 , + &chars , &style , &atsuLayout ); + + wxASSERT_MSG( status == noErr , wxT("couldn't create the layout of the text") ); + + status = ::ATSUSetTransientFontMatching( atsuLayout , true ); + wxASSERT_MSG( status == noErr , wxT("couldn't setup transient font matching") ); + + ATSUTextMeasurement textBefore, textAfter; + ATSUTextMeasurement textAscent, textDescent; + + status = ::ATSUGetUnjustifiedBounds( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd, + &textBefore , &textAfter, &textAscent , &textDescent ); + + if ( height ) + *height = FixedToInt(textAscent + textDescent); + if ( descent ) + *descent = FixedToInt(textDescent); + if ( externalLeading ) + *externalLeading = 0; + if ( width ) + *width = FixedToInt(textAfter - textBefore); + + ::ATSUDisposeTextLayout(atsuLayout); + + return; + } +#endif +#if wxMAC_USE_CG_TEXT + // TODO core graphics text implementation here +#endif +} + +void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const +{ + widths.Empty(); + widths.Add(0, text.length()); + + if (text.empty()) + return; + +#if wxMAC_USE_CORE_TEXT + { + wxMacCoreGraphicsFontData* fref = (wxMacCoreGraphicsFontData*)m_font.GetRefData(); + CTFontRef font = fref->GetCTFont(); + + wxCFStringRef t(text, wxLocale::GetSystemEncoding() ); + CFStringRef keys[] = { kCTFontAttributeName }; + CFTypeRef values[] = { font }; + wxCFRef attributes( CFDictionaryCreate(kCFAllocatorDefault, (const void**) &keys, (const void**) &values, + WXSIZEOF( keys ), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) ); + wxCFRef attrtext( CFAttributedStringCreate(kCFAllocatorDefault, t, attributes) ); + wxCFRef line( CTLineCreateWithAttributedString(attrtext) ); + + int chars = text.length(); + for ( int pos = 0; pos < (int)chars; pos ++ ) + { + widths[pos] = CTLineGetOffsetForStringIndex( line, pos+1 , NULL )+0.5; + } + + return; + } +#endif +#if wxMAC_USE_ATSU_TEXT + { + OSStatus status = noErr; + ATSUTextLayout atsuLayout; + wxMacUniCharBuffer unibuf( text ); + UniCharCount chars = unibuf.GetChars(); + + ATSUStyle style = (((wxMacCoreGraphicsFontData*)m_font.GetRefData())->GetATSUStyle()); + status = ::ATSUCreateTextLayoutWithTextPtr( unibuf.GetBuffer() , 0 , chars , chars , 1 , + &chars , &style , &atsuLayout ); + + wxASSERT_MSG( status == noErr , wxT("couldn't create the layout of the text") ); + + status = ::ATSUSetTransientFontMatching( atsuLayout , true ); + wxASSERT_MSG( status == noErr , wxT("couldn't setup transient font matching") ); + +// new implementation from JS, keep old one just in case +#if 0 + for ( int pos = 0; pos < (int)chars; pos ++ ) + { + unsigned long actualNumberOfBounds = 0; + ATSTrapezoid glyphBounds; + + // We get a single bound, since the text should only require one. If it requires more, there is an issue + OSStatus result; + result = ATSUGetGlyphBounds( atsuLayout, 0, 0, kATSUFromTextBeginning, pos + 1, + kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds ); + if (result != noErr || actualNumberOfBounds != 1 ) + return; + + widths[pos] = FixedToInt( glyphBounds.upperRight.x - glyphBounds.upperLeft.x ); + //unsigned char uch = s[i]; + } +#else + ATSLayoutRecord *layoutRecords = NULL; + ItemCount glyphCount = 0; + + // Get the glyph extents + OSStatus err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(atsuLayout, + 0, + kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void **) + &layoutRecords, + &glyphCount); + wxASSERT(glyphCount == (text.length()+1)); + + if ( err == noErr && glyphCount == (text.length()+1)) + { + for ( int pos = 1; pos < (int)glyphCount ; pos ++ ) + { + widths[pos-1] = FixedToInt( layoutRecords[pos].realPos ); + } + } + + ::ATSUDirectReleaseLayoutDataArrayPtr(NULL, + kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, + (void **) &layoutRecords); +#endif + ::ATSUDisposeTextLayout(atsuLayout); + } +#endif +#if wxMAC_USE_CG_TEXT + // TODO core graphics text implementation here +#endif +} + +void * wxMacCoreGraphicsContext::GetNativeContext() +{ + return m_cgContext; +} + +// concatenates this transform with the current transform of this context +void wxMacCoreGraphicsContext::ConcatTransform( const wxGraphicsMatrix& matrix ) +{ + if ( m_cgContext ) + CGContextConcatCTM( m_cgContext, *(CGAffineTransform*) matrix.GetNativeMatrix()); + else + m_windowTransform = CGAffineTransformConcat(m_windowTransform, *(CGAffineTransform*) matrix.GetNativeMatrix()); +} + +// sets the transform of this context +void wxMacCoreGraphicsContext::SetTransform( const wxGraphicsMatrix& matrix ) +{ + if ( m_cgContext ) + { + CGAffineTransform transform = CGContextGetCTM( m_cgContext ); + transform = CGAffineTransformInvert( transform ) ; + CGContextConcatCTM( m_cgContext, transform); + CGContextConcatCTM( m_cgContext, *(CGAffineTransform*) matrix.GetNativeMatrix()); + } + else + { + m_windowTransform = *(CGAffineTransform*) matrix.GetNativeMatrix(); + } +} + +// gets the matrix of this context +wxGraphicsMatrix wxMacCoreGraphicsContext::GetTransform() const +{ + wxGraphicsMatrix m = CreateMatrix(); + *((CGAffineTransform*) m.GetNativeMatrix()) = ( m_cgContext == NULL ? m_windowTransform : + CGContextGetCTM( m_cgContext )); + return m; +} + +// +// Renderer +// + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsRenderer declaration +//----------------------------------------------------------------------------- + +class WXDLLIMPEXP_CORE wxMacCoreGraphicsRenderer : public wxGraphicsRenderer +{ +public : + wxMacCoreGraphicsRenderer() {} + + virtual ~wxMacCoreGraphicsRenderer() {} + + // Context + + virtual wxGraphicsContext * CreateContext( const wxWindowDC& dc); + virtual wxGraphicsContext * CreateContext( const wxMemoryDC& dc); + virtual wxGraphicsContext * CreateContext( const wxPrinterDC& dc); + + virtual wxGraphicsContext * CreateContextFromNativeContext( void * context ); + + virtual wxGraphicsContext * CreateContextFromNativeWindow( void * window ); + + virtual wxGraphicsContext * CreateContext( wxWindow* window ); + + virtual wxGraphicsContext * CreateMeasuringContext(); + + // Path + + virtual wxGraphicsPath CreatePath(); + + // Matrix + + virtual wxGraphicsMatrix CreateMatrix( wxDouble a=1.0, wxDouble b=0.0, wxDouble c=0.0, wxDouble d=1.0, + wxDouble tx=0.0, wxDouble ty=0.0); + + + virtual wxGraphicsPen CreatePen(const wxPen& pen) ; + + virtual wxGraphicsBrush CreateBrush(const wxBrush& brush ) ; + + // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2 + virtual wxGraphicsBrush CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, + const wxColour&c1, const wxColour&c2) ; + + // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc) + // with radius r and color cColor + virtual wxGraphicsBrush CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, + const wxColour &oColor, const wxColour &cColor) ; + + // sets the font + virtual wxGraphicsFont CreateFont( const wxFont &font , const wxColour &col = *wxBLACK ) ; + + // create a native bitmap representation + virtual wxGraphicsBitmap CreateBitmap( const wxBitmap &bitmap ) ; + + // create a native bitmap representation + virtual wxGraphicsBitmap CreateSubBitmap( const wxGraphicsBitmap &bitmap, wxDouble x, wxDouble y, wxDouble w, wxDouble h ) ; +private : + DECLARE_DYNAMIC_CLASS_NO_COPY(wxMacCoreGraphicsRenderer) +} ; + +//----------------------------------------------------------------------------- +// wxMacCoreGraphicsRenderer implementation +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsRenderer,wxGraphicsRenderer) + +static wxMacCoreGraphicsRenderer gs_MacCoreGraphicsRenderer; + +wxGraphicsRenderer* wxGraphicsRenderer::GetDefaultRenderer() +{ + return &gs_MacCoreGraphicsRenderer; +} + +#ifdef __WXCOCOA__ +extern CGContextRef wxMacGetContextFromCurrentNSContext() ; +#endif + +wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContext( const wxWindowDC& dc ) +{ + const wxDCImpl* impl = dc.GetImpl(); + wxWindowDCImpl *win_impl = wxDynamicCast( impl, wxWindowDCImpl ); + if (win_impl) + { + int w, h; + win_impl->GetSize( &w, &h ); + CGContextRef cgctx = 0; +#ifdef __WXMAC__ + cgctx = (CGContextRef)(win_impl->GetWindow()->MacGetCGContextRef()); +#else + cgctx = wxMacGetContextFromCurrentNSContext() ; +#endif + return new wxMacCoreGraphicsContext( this, cgctx, (wxDouble) w, (wxDouble) h ); + } + return NULL; +} + +wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContext( const wxMemoryDC& dc ) +{ +#ifdef __WXMAC__ + const wxDCImpl* impl = dc.GetImpl(); + wxMemoryDCImpl *mem_impl = wxDynamicCast( impl, wxMemoryDCImpl ); + if (mem_impl) + { + int w, h; + mem_impl->GetSize( &w, &h ); + return new wxMacCoreGraphicsContext( this, + (CGContextRef)(mem_impl->GetGraphicsContext()->GetNativeContext()), (wxDouble) w, (wxDouble) h ); + } +#endif + return NULL; +} + +wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContext( const wxPrinterDC& dc ) +{ +#ifdef __WXMAC__ + const wxDCImpl* impl = dc.GetImpl(); + wxPrinterDCImpl *print_impl = wxDynamicCast( impl, wxPrinterDCImpl ); + if (print_impl) + { + int w, h; + print_impl->GetSize( &w, &h ); + return new wxMacCoreGraphicsContext( this, + (CGContextRef)(print_impl->GetGraphicsContext()->GetNativeContext()), (wxDouble) w, (wxDouble) h ); + } +#endif + return NULL; +} + +wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContextFromNativeContext( void * context ) +{ + return new wxMacCoreGraphicsContext(this,(CGContextRef)context); +} + + +wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContextFromNativeWindow( void * window ) +{ + return new wxMacCoreGraphicsContext(this,(WindowRef)window); +} + +wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateContext( wxWindow* window ) +{ + return new wxMacCoreGraphicsContext(this, window ); +} + +wxGraphicsContext * wxMacCoreGraphicsRenderer::CreateMeasuringContext() +{ + return new wxMacCoreGraphicsContext(this); +} + +// Path + +wxGraphicsPath wxMacCoreGraphicsRenderer::CreatePath() +{ + wxGraphicsPath m; + m.SetRefData( new wxMacCoreGraphicsPathData(this)); + return m; +} + + +// Matrix + +wxGraphicsMatrix wxMacCoreGraphicsRenderer::CreateMatrix( wxDouble a, wxDouble b, wxDouble c, wxDouble d, + wxDouble tx, wxDouble ty) +{ + wxGraphicsMatrix m; + wxMacCoreGraphicsMatrixData* data = new wxMacCoreGraphicsMatrixData( this ); + data->Set( a,b,c,d,tx,ty ) ; + m.SetRefData(data); + return m; +} + +wxGraphicsPen wxMacCoreGraphicsRenderer::CreatePen(const wxPen& pen) +{ + if ( !pen.Ok() || pen.GetStyle() == wxTRANSPARENT ) + return wxNullGraphicsPen; + else + { + wxGraphicsPen p; + p.SetRefData(new wxMacCoreGraphicsPenData( this, pen )); + return p; + } +} + +wxGraphicsBrush wxMacCoreGraphicsRenderer::CreateBrush(const wxBrush& brush ) +{ + if ( !brush.Ok() || brush.GetStyle() == wxTRANSPARENT ) + return wxNullGraphicsBrush; + else + { + wxGraphicsBrush p; + p.SetRefData(new wxMacCoreGraphicsBrushData( this, brush )); + return p; + } +} + +wxGraphicsBitmap wxMacCoreGraphicsRenderer::CreateBitmap( const wxBitmap& bmp ) +{ + if ( bmp.Ok() ) + { + wxGraphicsBitmap p; +#ifdef __WXMAC__ + p.SetRefData(new wxMacCoreGraphicsBitmapData( this , bmp.CreateCGImage(), bmp.GetDepth() == 1 ) ); +#endif + return p; + } + else + return wxNullGraphicsBitmap; +} + +wxGraphicsBitmap wxMacCoreGraphicsRenderer::CreateSubBitmap( const wxGraphicsBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h ) +{ + wxMacCoreGraphicsBitmapData* refdata =static_cast(bmp.GetRefData()); + CGImageRef img = refdata->GetBitmap(); + if ( img ) + { + wxGraphicsBitmap p; + CGImageRef subimg = CGImageCreateWithImageInRect(img,CGRectMake( (CGFloat) x , (CGFloat) y , (CGFloat) w , (CGFloat) h )); + p.SetRefData(new wxMacCoreGraphicsBitmapData( this , subimg, refdata->IsMonochrome() ) ); + return p; + } + else + return wxNullGraphicsBitmap; +} + +// sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2 +wxGraphicsBrush wxMacCoreGraphicsRenderer::CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2, + const wxColour&c1, const wxColour&c2) +{ + wxGraphicsBrush p; + wxMacCoreGraphicsBrushData* d = new wxMacCoreGraphicsBrushData( this ); + d->CreateLinearGradientBrush(x1, y1, x2, y2, c1, c2); + p.SetRefData(d); + return p; +} + +// sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc) +// with radius r and color cColor +wxGraphicsBrush wxMacCoreGraphicsRenderer::CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius, + const wxColour &oColor, const wxColour &cColor) +{ + wxGraphicsBrush p; + wxMacCoreGraphicsBrushData* d = new wxMacCoreGraphicsBrushData( this ); + d->CreateRadialGradientBrush(xo,yo,xc,yc,radius,oColor,cColor); + p.SetRefData(d); + return p; +} + +// sets the font +wxGraphicsFont wxMacCoreGraphicsRenderer::CreateFont( const wxFont &font , const wxColour &col ) +{ + if ( font.Ok() ) + { + wxGraphicsFont p; + p.SetRefData(new wxMacCoreGraphicsFontData( this , font, col )); + return p; + } + else + return wxNullGraphicsFont; +} + +// +// CoreGraphics Helper Methods +// + +// Data Providers and Consumers + +size_t UMAPutBytesCFRefCallback( void *info, const void *bytes, size_t count ) +{ + CFMutableDataRef data = (CFMutableDataRef) info; + if ( data ) + { + CFDataAppendBytes( data, (const UInt8*) bytes, count ); + } + return count; +} + +void wxMacReleaseCFDataProviderCallback(void *info, + const void *WXUNUSED(data), + size_t WXUNUSED(count)) +{ + if ( info ) + CFRelease( (CFDataRef) info ); +} + +void wxMacReleaseCFDataConsumerCallback( void *info ) +{ + if ( info ) + CFRelease( (CFDataRef) info ); +} + +CGDataProviderRef wxMacCGDataProviderCreateWithCFData( CFDataRef data ) +{ + if ( data == NULL ) + return NULL; + + return CGDataProviderCreateWithCFData( data ); +} + +CGDataConsumerRef wxMacCGDataConsumerCreateWithCFData( CFMutableDataRef data ) +{ + if ( data == NULL ) + return NULL; + + return CGDataConsumerCreateWithCFData( data ); +} + +void +wxMacReleaseMemoryBufferProviderCallback(void *info, + const void * WXUNUSED_UNLESS_DEBUG(data), + size_t WXUNUSED(size)) +{ + wxMemoryBuffer* membuf = (wxMemoryBuffer*) info ; + + wxASSERT( data == membuf->GetData() ) ; + + delete membuf ; +} + +CGDataProviderRef wxMacCGDataProviderCreateWithMemoryBuffer( const wxMemoryBuffer& buf ) +{ + wxMemoryBuffer* b = new wxMemoryBuffer( buf ); + if ( b->GetDataLen() == 0 ) + return NULL; + + return CGDataProviderCreateWithData( b , (const void *) b->GetData() , b->GetDataLen() , + wxMacReleaseMemoryBufferProviderCallback ); +} diff --git a/src/osx/carbon/helpxxxx.cpp b/src/osx/carbon/helpxxxx.cpp new file mode 100644 index 0000000000..984e0444de --- /dev/null +++ b/src/osx/carbon/helpxxxx.cpp @@ -0,0 +1,80 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: helpxxxx.cpp +// Purpose: Help system: native implementation +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/stubs/helpxxxx.h" + +#include + +IMPLEMENT_DYNAMIC_CLASS(wxXXXXHelpController, wxHelpControllerBase) + +wxXXXXHelpController::wxXXXXHelpController() +{ + m_helpFile = ""; +} + +wxXXXXHelpController::~wxXXXXHelpController() +{ +} + +bool wxXXXXHelpController::Initialize(const wxString& filename) +{ + m_helpFile = filename; + // TODO any other inits + return TRUE; +} + +bool wxXXXXHelpController::LoadFile(const wxString& file) +{ + m_helpFile = file; + // TODO + return TRUE; +} + +bool wxXXXXHelpController::DisplayContents() +{ + // TODO + return FALSE; +} + +bool wxXXXXHelpController::DisplaySection(int section) +{ + // TODO + return FALSE; +} + +bool wxXXXXHelpController::DisplayBlock(long block) +{ + // TODO + return FALSE; +} + +bool wxXXXXHelpController::KeywordSearch(const wxString& k, + wxHelpSearchMode WXUNUSED(mode)) +{ + if (m_helpFile == "") return FALSE; + + // TODO + return FALSE; +} + +// Can't close the help window explicitly in WinHelp +bool wxXXXXHelpController::Quit() +{ + // TODO + return FALSE; +} + +void wxXXXXHelpController::OnQuit() +{ +} + diff --git a/src/osx/carbon/icon.cpp b/src/osx/carbon/icon.cpp new file mode 100644 index 0000000000..c90a0b37c7 --- /dev/null +++ b/src/osx/carbon/icon.cpp @@ -0,0 +1,317 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/icon.cpp +// Purpose: wxIcon class +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/icon.h" + +#ifndef WX_PRECOMP + #include "wx/image.h" +#endif + +#include "wx/mac/private.h" + +IMPLEMENT_DYNAMIC_CLASS(wxIcon, wxGDIObject) + +#define M_ICONDATA ((wxIconRefData *)m_refData) + +class WXDLLEXPORT wxIconRefData : public wxGDIRefData +{ +public: + wxIconRefData() { Init(); } + wxIconRefData( WXHICON iconref, int desiredWidth, int desiredHeight ); + virtual ~wxIconRefData() { Free(); } + + virtual bool IsOk() const { return m_iconRef != NULL; } + + virtual void Free(); + + void SetWidth( int width ) { m_width = width; } + void SetHeight( int height ) { m_height = height; } + + int GetWidth() const { return m_width; } + int GetHeight() const { return m_height; } + + WXHICON GetHICON() const { return (WXHICON) m_iconRef; } + +private: + void Init(); + + IconRef m_iconRef; + int m_width; + int m_height; +}; + + +wxIconRefData::wxIconRefData( WXHICON icon, int desiredWidth, int desiredHeight ) +{ + m_iconRef = MAC_WXHICON( icon ) ; + + // Standard sizes + SetWidth( desiredWidth == -1 ? 32 : desiredWidth ) ; + SetHeight( desiredHeight == -1 ? 32 : desiredHeight ) ; +} + +void wxIconRefData::Init() +{ + m_iconRef = NULL ; + m_width = + m_height = 0; +} + +void wxIconRefData::Free() +{ + if ( m_iconRef ) + { + ReleaseIconRef( m_iconRef ) ; + m_iconRef = NULL ; + } +} + +// +// +// + +wxIcon::wxIcon() +{ +} + +wxIcon::wxIcon( const char bits[], int width, int height ) +{ + wxBitmap bmp( bits, width, height ) ; + CopyFromBitmap( bmp ) ; +} + +wxIcon::wxIcon(const char* const* bits) +{ + wxBitmap bmp( bits ) ; + CopyFromBitmap( bmp ) ; +} + +wxIcon::wxIcon( + const wxString& icon_file, int flags, + int desiredWidth, int desiredHeight ) +{ + LoadFile( icon_file, (wxBitmapType) flags, desiredWidth, desiredHeight ); +} + +wxIcon::wxIcon(WXHICON icon, const wxSize& size) + : wxGDIObject() +{ + // as the icon owns that ref, we have to acquire it as well + if (icon) + AcquireIconRef( (IconRef) icon ) ; + + m_refData = new wxIconRefData( icon, size.x, size.y ) ; +} + +wxIcon::~wxIcon() +{ +} + +wxGDIRefData *wxIcon::CreateGDIRefData() const +{ + return new wxIconRefData; +} + +wxGDIRefData *wxIcon::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxIconRefData(*wx_static_cast(const wxIconRefData *, data)); +} + +WXHICON wxIcon::GetHICON() const +{ + wxASSERT( Ok() ) ; + + return (WXHICON) ((wxIconRefData*)m_refData)->GetHICON() ; +} + +int wxIcon::GetWidth() const +{ + wxCHECK_MSG( Ok(), -1, wxT("invalid icon") ); + + return M_ICONDATA->GetWidth(); +} + +int wxIcon::GetHeight() const +{ + wxCHECK_MSG( Ok(), -1, wxT("invalid icon") ); + + return M_ICONDATA->GetHeight(); +} + +int wxIcon::GetDepth() const +{ + return 32; +} + +void wxIcon::SetDepth( int WXUNUSED(depth) ) +{ +} + +void wxIcon::SetWidth( int WXUNUSED(width) ) +{ +} + +void wxIcon::SetHeight( int WXUNUSED(height) ) +{ +} + +bool wxIcon::LoadFile( + const wxString& filename, wxBitmapType type, + int desiredWidth, int desiredHeight ) +{ + UnRef(); + + if ( type == wxBITMAP_TYPE_ICON_RESOURCE ) + { + OSType theId = 0 ; + + if ( filename == wxT("wxICON_INFORMATION") ) + { + theId = kAlertNoteIcon ; + } + else if ( filename == wxT("wxICON_QUESTION") ) + { + theId = kAlertCautionIcon ; + } + else if ( filename == wxT("wxICON_WARNING") ) + { + theId = kAlertCautionIcon ; + } + else if ( filename == wxT("wxICON_ERROR") ) + { + theId = kAlertStopIcon ; + } + else if ( filename == wxT("wxICON_FOLDER") ) + { + theId = kGenericFolderIcon ; + } + else if ( filename == wxT("wxICON_FOLDER_OPEN") ) + { + theId = kOpenFolderIcon ; + } + else if ( filename == wxT("wxICON_NORMAL_FILE") ) + { + theId = kGenericDocumentIcon ; + } + else + { + IconRef iconRef = NULL ; + + // first look in the resource fork + if ( iconRef == NULL ) + { + Str255 theName ; + + wxMacStringToPascal( filename , theName ) ; + Handle resHandle = GetNamedResource( 'icns' , theName ) ; + if ( resHandle != 0L ) + { + IconFamilyHandle iconFamily = (IconFamilyHandle) resHandle ; + HLock((Handle) iconFamily); + OSStatus err = GetIconRefFromIconFamilyPtr( *iconFamily, GetHandleSize((Handle) iconFamily), &iconRef ); + HUnlock((Handle) iconFamily); + if ( err != noErr ) + { + wxFAIL_MSG("Error when constructing icon ref"); + } + + ReleaseResource( resHandle ) ; + } + } + if ( iconRef == NULL ) + { + // TODO add other attempts to load it from files etc here + } + if ( iconRef ) + { + m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ) ; + return true ; + } + } + + if ( theId != 0 ) + { + IconRef iconRef = NULL ; + verify_noerr( GetIconRef( kOnSystemDisk, kSystemIconsCreator, theId, &iconRef ) ) ; + if ( iconRef ) + { + m_refData = new wxIconRefData( (WXHICON) iconRef, desiredWidth, desiredHeight ) ; + + return true ; + } + } + + return false ; + } + else + { + wxBitmapHandler *handler = wxBitmap::FindHandler( type ); + + if ( handler ) + { + wxBitmap bmp ; + if ( handler->LoadFile( &bmp , filename, type, desiredWidth, desiredHeight )) + { + CopyFromBitmap( bmp ) ; + + return true ; + } + + return false ; + } + else + { +#if wxUSE_IMAGE + wxImage loadimage( filename, type ); + if (loadimage.Ok()) + { + if ( desiredWidth == -1 ) + desiredWidth = loadimage.GetWidth() ; + if ( desiredHeight == -1 ) + desiredHeight = loadimage.GetHeight() ; + if ( desiredWidth != loadimage.GetWidth() || desiredHeight != loadimage.GetHeight() ) + loadimage.Rescale( desiredWidth , desiredHeight ) ; + + wxBitmap bmp( loadimage ); + CopyFromBitmap( bmp ) ; + + return true; + } +#endif + } + } + return true ; +} + +void wxIcon::CopyFromBitmap( const wxBitmap& bmp ) +{ + UnRef() ; + + // as the bitmap owns that ref, we have to acquire it as well + IconRef iconRef = bmp.CreateIconRef() ; + m_refData = new wxIconRefData( (WXHICON) iconRef, bmp.GetWidth(), bmp.GetHeight() ) ; +} + +IMPLEMENT_DYNAMIC_CLASS(wxICONResourceHandler, wxBitmapHandler) + +bool wxICONResourceHandler::LoadFile( + wxBitmap *bitmap, const wxString& name, long WXUNUSED(flags), + int desiredWidth, int desiredHeight ) +{ + wxIcon icon ; + icon.LoadFile( name , wxBITMAP_TYPE_ICON_RESOURCE , desiredWidth , desiredHeight ) ; + bitmap->CopyFromIcon( icon ) ; + + return bitmap->Ok() ; +} + diff --git a/src/osx/carbon/imaglist.cpp b/src/osx/carbon/imaglist.cpp new file mode 100644 index 0000000000..054cdeb734 --- /dev/null +++ b/src/osx/carbon/imaglist.cpp @@ -0,0 +1,309 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/imaglist.cpp +// Purpose: +// Author: Robert Roebling +// RCS_ID: $Id$ +// Copyright: (c) 1998 Robert Roebling +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_IMAGLIST + +#include "wx/imaglist.h" + +#ifndef WX_PRECOMP + #include "wx/dc.h" + #include "wx/icon.h" + #include "wx/image.h" +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxImageList, wxObject) + + +wxImageList::wxImageList( int width, int height, bool mask, int initialCount ) +{ + (void)Create(width, height, mask, initialCount); +} + +wxImageList::~wxImageList() +{ + (void)RemoveAll(); +} + +int wxImageList::GetImageCount() const +{ + return m_images.GetCount(); +} + +bool wxImageList::Create( int width, int height, bool WXUNUSED(mask), int WXUNUSED(initialCount) ) +{ + m_width = width; + m_height = height; + + return Create(); +} + +bool wxImageList::Create() +{ + return true; +} + +int wxImageList::Add( const wxIcon &bitmap ) +{ + wxASSERT_MSG( (bitmap.GetWidth() == m_width && bitmap.GetHeight() == m_height) + || (m_width == 0 && m_height == 0), + _T("invalid bitmap size in wxImageList: this might work ") + _T("on this platform but definitely won't under Windows.") ); + + m_images.Append( new wxIcon( bitmap ) ); + + if (m_width == 0 && m_height == 0) + { + m_width = bitmap.GetWidth(); + m_height = bitmap.GetHeight(); + } + + return m_images.GetCount() - 1; +} + +int wxImageList::Add( const wxBitmap &bitmap ) +{ + wxASSERT_MSG( (bitmap.GetWidth() >= m_width && bitmap.GetHeight() == m_height) + || (m_width == 0 && m_height == 0), + _T("invalid bitmap size in wxImageList: this might work ") + _T("on this platform but definitely won't under Windows.") ); + + // Mimic behavior of Windows ImageList_Add that automatically breaks up the added + // bitmap into sub-images of the correct size + if (m_width > 0 && bitmap.GetWidth() > m_width && bitmap.GetHeight() >= m_height) + { + int numImages = bitmap.GetWidth() / m_width; + for (int subIndex = 0; subIndex < numImages; subIndex++) + { + wxRect rect(m_width * subIndex, 0, m_width, m_height); + wxBitmap tmpBmp = bitmap.GetSubBitmap(rect); + m_images.Append( new wxBitmap(tmpBmp) ); + } + } + else + { + m_images.Append( new wxBitmap(bitmap) ); + } + + if (m_width == 0 && m_height == 0) + { + m_width = bitmap.GetWidth(); + m_height = bitmap.GetHeight(); + } + + return m_images.GetCount() - 1; +} + +int wxImageList::Add( const wxBitmap& bitmap, const wxBitmap& mask ) +{ + wxBitmap bmp( bitmap ); + if (mask.Ok()) + bmp.SetMask( new wxMask( mask ) ); + + return Add( bmp ); +} + +int wxImageList::Add( const wxBitmap& bitmap, const wxColour& maskColour ) +{ + wxImage img = bitmap.ConvertToImage(); + img.SetMaskColour( maskColour.Red(), maskColour.Green(), maskColour.Blue() ); + + return Add( wxBitmap( img ) ); +} + +// Get the bitmap +wxBitmap wxImageList::GetBitmap(int index) const +{ + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, wxNullBitmap , wxT("wrong index in image list") ); + + wxObject* obj = (wxObject*) node->GetData(); + if ( obj == NULL ) + return wxNullBitmap ; + else if ( obj->IsKindOf(CLASSINFO(wxIcon)) ) + return wxBitmap( *(wx_static_cast(wxIcon*, obj)) ) ; + else + return *(wx_static_cast(wxBitmap*, obj)) ; +} + +// Get the icon +wxIcon wxImageList::GetIcon(int index) const +{ + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, wxNullIcon , wxT("wrong index in image list") ); + + wxObject* obj = (wxObject*) node->GetData(); + if ( obj == NULL ) + return wxNullIcon ; + else if ( obj->IsKindOf(CLASSINFO(wxBitmap)) ) + { + wxFAIL_MSG( wxT("cannot convert from bitmap to icon") ) ; + return wxNullIcon ; + } + else + return *(wx_static_cast(wxIcon*, obj)) ; +} + +bool wxImageList::Replace( int index, const wxBitmap &bitmap ) +{ + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, false, wxT("wrong index in image list") ); + + wxBitmap* newBitmap = new wxBitmap( bitmap ); + + if (index == (int) m_images.GetCount() - 1) + { + delete node->GetData(); + + m_images.Erase( node ); + m_images.Append( newBitmap ); + } + else + { + wxList::compatibility_iterator next = node->GetNext(); + delete node->GetData(); + + m_images.Erase( node ); + m_images.Insert( next, newBitmap ); + } + + return true; +} + +bool wxImageList::Replace( int index, const wxIcon &bitmap ) +{ + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, false, wxT("wrong index in image list") ); + + wxIcon* newBitmap = new wxIcon( bitmap ); + + if (index == (int) m_images.GetCount() - 1) + { + delete node->GetData(); + m_images.Erase( node ); + m_images.Append( newBitmap ); + } + else + { + wxList::compatibility_iterator next = node->GetNext(); + delete node->GetData(); + m_images.Erase( node ); + m_images.Insert( next, newBitmap ); + } + + return true; +} + +bool wxImageList::Replace( int index, const wxBitmap &bitmap, const wxBitmap &mask ) +{ + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, false, wxT("wrong index in image list") ); + + wxBitmap* newBitmap = new wxBitmap(bitmap); + + if (index == (int) m_images.GetCount() - 1) + { + delete node->GetData(); + m_images.Erase( node ); + m_images.Append( newBitmap ); + } + else + { + wxList::compatibility_iterator next = node->GetNext(); + delete node->GetData(); + m_images.Erase( node ); + m_images.Insert( next, newBitmap ); + } + + if (mask.Ok()) + newBitmap->SetMask(new wxMask(mask)); + + return true; +} + +bool wxImageList::Remove( int index ) +{ + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, false, wxT("wrong index in image list") ); + + delete node->GetData(); + m_images.Erase( node ); + + return true; +} + +bool wxImageList::RemoveAll() +{ + WX_CLEAR_LIST(wxList, m_images); + m_images.Clear(); + + return true; +} + +bool wxImageList::GetSize( int index, int &width, int &height ) const +{ + width = 0; + height = 0; + + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, false, wxT("wrong index in image list") ); + + wxObject *obj = (wxObject*)node->GetData(); + if (obj->IsKindOf(CLASSINFO(wxIcon))) + { + wxIcon *bm = wx_static_cast( wxIcon* , obj ) ; + width = bm->GetWidth(); + height = bm->GetHeight(); + } + else + { + wxBitmap *bm = wx_static_cast( wxBitmap* , obj ) ; + width = bm->GetWidth(); + height = bm->GetHeight(); + } + + return true; +} + +bool wxImageList::Draw( + int index, wxDC &dc, int x, int y, + int flags, bool WXUNUSED(solidBackground) ) +{ + wxList::compatibility_iterator node = m_images.Item( index ); + + wxCHECK_MSG( node, false, wxT("wrong index in image list") ); + + wxObject *obj = (wxObject*)node->GetData(); + if (obj->IsKindOf(CLASSINFO(wxIcon))) + { + wxIcon *bm = wx_static_cast( wxIcon* , obj ) ; + dc.DrawIcon( *bm , x, y ); + } + else + { + wxBitmap *bm = wx_static_cast( wxBitmap* , obj ) ; + dc.DrawBitmap( *bm, x, y, (flags & wxIMAGELIST_DRAW_TRANSPARENT) > 0 ); + } + + return true; +} + +#endif // wxUSE_IMAGLIST diff --git a/src/osx/carbon/joystick.cpp b/src/osx/carbon/joystick.cpp new file mode 100644 index 0000000000..2faa009f42 --- /dev/null +++ b/src/osx/carbon/joystick.cpp @@ -0,0 +1 @@ +// todo remove \ No newline at end of file diff --git a/src/osx/carbon/listbox.cpp b/src/osx/carbon/listbox.cpp new file mode 100644 index 0000000000..8c479b1066 --- /dev/null +++ b/src/osx/carbon/listbox.cpp @@ -0,0 +1,628 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/listbox.cpp +// Purpose: wxListBox +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_LISTBOX + +#include "wx/listbox.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/intl.h" + #include "wx/utils.h" + #include "wx/settings.h" + #include "wx/arrstr.h" + #include "wx/dcclient.h" +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems) + +BEGIN_EVENT_TABLE(wxListBox, wxControl) +END_EVENT_TABLE() + +#include "wx/mac/uma.h" + +// ============================================================================ +// list box control implementation +// ============================================================================ + +wxListBox::wxListBox() +{ +} + +bool wxListBox::Create( + wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + const wxArrayString& choices, + long style, + const wxValidator& validator, + const wxString& name ) +{ + wxCArrayString chs(choices); + + return Create( + parent, id, pos, size, chs.GetCount(), chs.GetStrings(), + style, validator, name ); +} + +wxMacListControl* wxListBox::GetPeer() const +{ + wxMacDataBrowserListControl *lb = wxDynamicCast(m_peer,wxMacDataBrowserListControl); + return lb ? wx_static_cast(wxMacListControl*,lb) : 0 ; +} + +bool wxListBox::Create( + wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + int n, + const wxString choices[], + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_blockEvents = false; + m_macIsUserPane = false; + + wxASSERT_MSG( !(style & wxLB_MULTIPLE) || !(style & wxLB_EXTENDED), + wxT("only a single listbox selection mode can be specified") ); + + if ( !wxListBoxBase::Create( parent, id, pos, size, style & ~(wxHSCROLL | wxVSCROLL), validator, name ) ) + return false; + + wxMacDataBrowserListControl* control = new wxMacDataBrowserListControl( this, pos, size, style ); + // TODO CHECK control->SetClientDataType( m_clientDataItemsType ); + m_peer = control; + + MacPostControlCreate( pos, size ); + + Append(n, choices); + + // Needed because it is a wxControlWithItems + SetInitialSize( size ); + + return true; +} + +wxListBox::~wxListBox() +{ + FreeData(); + m_peer->SetReference( 0 ); +} + +void wxListBox::FreeData() +{ + GetPeer()->MacClear(); +} + +void wxListBox::DoSetFirstItem(int n) +{ + GetPeer()->MacScrollTo( n ); +} + +void wxListBox::EnsureVisible(int n) +{ + GetPeer()->MacScrollTo( n ); +} + +void wxListBox::DoDeleteOneItem(unsigned int n) +{ + wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::Delete") ); + + m_blockEvents = true; + GetPeer()->MacDelete( n ); + m_blockEvents = false; + + UpdateOldSelections(); +} + +int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items, + unsigned int pos, + void **clientData, + wxClientDataType type) +{ + InvalidateBestSize(); + + GetPeer()->MacInsert( pos, items ); + const unsigned int count = items.GetCount(); + if ( clientData ) + { + for (unsigned int i = 0; i < count; ++i) + AssignNewItemClientData( pos + i, clientData, i, type ); + } + + return pos + count - 1; + + UpdateOldSelections(); +} + +int wxListBox::FindString(const wxString& s, bool bCase) const +{ + for ( size_t i = 0; i < GetCount(); ++ i ) + { + if (s.IsSameAs( GetString( i ), bCase) ) + return (int)i; + } + + return wxNOT_FOUND; +} + +void wxListBox::DoClear() +{ + m_blockEvents = true; + FreeData(); + m_blockEvents = false; + + UpdateOldSelections(); +} + +void wxListBox::DoSetSelection(int n, bool select) +{ + wxCHECK_RET( n == wxNOT_FOUND || IsValid(n), + wxT("invalid index in wxListBox::SetSelection") ); + + m_blockEvents = true; + + if ( n == wxNOT_FOUND ) + GetPeer()->MacDeselectAll(); + else + GetPeer()->MacSetSelection( n, select, HasMultipleSelection() ); + + m_blockEvents = false; + + UpdateOldSelections(); +} + +bool wxListBox::IsSelected(int n) const +{ + wxCHECK_MSG( IsValid(n), false, wxT("invalid index in wxListBox::Selected") ); + + return GetPeer()->MacIsSelected( n ); +} + +void *wxListBox::DoGetItemClientData(unsigned int n) const +{ + wxCHECK_MSG( IsValid(n), NULL, wxT("invalid index in wxListBox::GetClientData")); + return GetPeer()->MacGetClientData( n ); +} + +void wxListBox::DoSetItemClientData(unsigned int n, void *clientData) +{ + wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetClientData") ); + GetPeer()->MacSetClientData( n , clientData); +} + +// Return number of selections and an array of selected integers +int wxListBox::GetSelections(wxArrayInt& aSelections) const +{ + return GetPeer()->MacGetSelections( aSelections ); +} + +// Get single selection, for single choice list items +int wxListBox::GetSelection() const +{ + return GetPeer()->MacGetSelection(); +} + +// Find string for position +wxString wxListBox::GetString(unsigned int n) const +{ + wxCHECK_MSG( IsValid(n), wxEmptyString, wxT("invalid index in wxListBox::GetString") ); + return GetPeer()->MacGetString(n); +} + +void wxListBox::SetString(unsigned int n, const wxString& s) +{ + GetPeer()->MacSetString( n, s ); +} + +wxSize wxListBox::DoGetBestSize() const +{ + int lbWidth = 100; // some defaults + int lbHeight = 110; + int wLine; + + { + wxClientDC dc(const_cast(this)); + dc.SetFont(GetFont()); + + // Find the widest line + for (unsigned int i = 0; i < GetCount(); i++) + { + wxString str( GetString( i ) ); + + wxCoord width, height ; + dc.GetTextExtent( str , &width, &height); + wLine = width ; + lbWidth = wxMax( lbWidth, wLine ); + } + + // Add room for the scrollbar + lbWidth += wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ); + + // And just a bit more + int cy = 12; + + wxCoord width, height ; + dc.GetTextExtent( wxT("XX") , &width, &height); + int cx = width ; + lbWidth += cx; + + // don't make the listbox too tall (limit height to around 10 items) + // but don't make it too small neither + lbHeight = wxMax( (cy + 4) * wxMin( wxMax( GetCount(), 3 ), 10 ), 70 ); + } + + return wxSize( lbWidth, lbHeight ); +} + +unsigned int wxListBox::GetCount() const +{ + return GetPeer()->MacGetCount(); +} + +void wxListBox::Refresh(bool eraseBack, const wxRect *rect) +{ + wxControl::Refresh( eraseBack, rect ); +} + +// Some custom controls depend on this +/* static */ wxVisualAttributes +wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) +{ + wxVisualAttributes attr; + + attr.colFg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ); + attr.colBg = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX ); + attr.font.MacCreateFromThemeFont(kThemeViewsFont); + + return attr; +} + +int wxListBox::DoListHitTest(const wxPoint& inpoint) const +{ + OSStatus err; + + // There are few reasons why this is complicated: + // 1) There is no native HitTest function for Mac + // 2) GetDataBrowserItemPartBounds only works on visible items + // 3) We can't do it through GetDataBrowserTableView[Item]RowHeight + // because what it returns is basically inaccurate in the context + // of the coordinates we want here, but we use this as a guess + // for where the first visible item lies + + wxPoint point = inpoint; + + // get column property ID (req. for call to itempartbounds) + DataBrowserTableViewColumnID colId = 0; + err = GetDataBrowserTableViewColumnProperty(m_peer->GetControlRef(), 0, &colId); + wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserTableViewColumnProperty")); + + // OK, first we need to find the first visible item we have - + // this will be the "low" for our binary search. There is no real + // easy way around this, as we will need to do a SLOW linear search + // until we find a visible item, but we can do a cheap calculation + // via the row height to speed things up a bit + UInt32 scrollx, scrolly; + err = GetDataBrowserScrollPosition(m_peer->GetControlRef(), &scrollx, &scrolly); + wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserScrollPosition")); + + UInt16 height; + err = GetDataBrowserTableViewRowHeight(m_peer->GetControlRef(), &height); + wxCHECK_MSG(err == noErr, wxNOT_FOUND, wxT("Unexpected error from GetDataBrowserTableViewRowHeight")); + + // these indices are 0-based, as usual, so we need to add 1 to them when + // passing them to data browser functions which use 1-based indices + int low = scrolly / height, + high = GetCount() - 1; + + // search for the first visible item (note that the scroll guess above + // is the low bounds of where the item might lie so we only use that as a + // starting point - we should reach it within 1 or 2 iterations of the loop) + while ( low <= high ) + { + Rect bounds; + err = GetDataBrowserItemPartBounds( + m_peer->GetControlRef(), low + 1, colId, + kDataBrowserPropertyEnclosingPart, + &bounds); // note +1 to translate to Mac ID + if ( err == noErr ) + break; + + // errDataBrowserItemNotFound is expected as it simply means that the + // item is not currently visible -- but other errors are not + wxCHECK_MSG( err == errDataBrowserItemNotFound, wxNOT_FOUND, + wxT("Unexpected error from GetDataBrowserItemPartBounds") ); + + low++; + } + + // NOW do a binary search for where the item lies, searching low again if + // we hit an item that isn't visible + while ( low <= high ) + { + int mid = (low + high) / 2; + + Rect bounds; + err = GetDataBrowserItemPartBounds( + m_peer->GetControlRef(), mid + 1, colId, + kDataBrowserPropertyEnclosingPart, + &bounds); //note +1 to trans to mac id + wxCHECK_MSG( err == noErr || err == errDataBrowserItemNotFound, + wxNOT_FOUND, + wxT("Unexpected error from GetDataBrowserItemPartBounds") ); + + if ( err == errDataBrowserItemNotFound ) + { + // item not visible, attempt to find a visible one + // search lower + high = mid - 1; + } + else // visible item, do actual hitttest + { + // if point is within the bounds, return this item (since we assume + // all x coords of items are equal we only test the x coord in + // equality) + if ((point.x >= bounds.left && point.x <= bounds.right) && + (point.y >= bounds.top && point.y <= bounds.bottom) ) + { + // found! + return mid; + } + + if ( point.y < bounds.top ) + // index(bounds) greater then key(point) + high = mid - 1; + else + // index(bounds) less then key(point) + low = mid + 1; + } + } + + return wxNOT_FOUND; +} + +// ============================================================================ +// data browser based implementation +// ============================================================================ + +wxMacListBoxItem::wxMacListBoxItem() + :wxMacDataItem() +{ +} + +wxMacListBoxItem::~wxMacListBoxItem() +{ +} + +void wxMacListBoxItem::Notification(wxMacDataItemBrowserControl *owner , + DataBrowserItemNotification message, + DataBrowserItemDataRef WXUNUSED(itemData) ) const +{ + wxMacDataBrowserListControl *lb = wxDynamicCast(owner,wxMacDataBrowserListControl); + + // we want to depend on as little as possible to make sure tear-down of controls is safe + + if ( message == kDataBrowserItemRemoved) + { + if ( lb != NULL && lb->GetClientDataType() == wxClientData_Object ) + { + delete (wxClientData*) (m_data); + } + + delete this; + return; + } + + wxListBox *list = wxDynamicCast( owner->GetPeer() , wxListBox ); + wxCHECK_RET( list != NULL , wxT("Listbox expected")); + + if (message == kDataBrowserItemDoubleClicked) + { + wxCommandEvent event( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, list->GetId() ); + event.SetEventObject( list ); + if ( list->HasClientObjectData() ) + event.SetClientObject( (wxClientData*) m_data ); + else if ( list->HasClientUntypedData() ) + event.SetClientData( m_data ); + event.SetString( m_label ); + event.SetInt( owner->GetLineFromItem( this ) ); + event.SetExtraLong( 1 ); + list->HandleWindowEvent(event); + return; + } +} + +IMPLEMENT_DYNAMIC_CLASS( wxMacDataBrowserListControl , wxMacDataItemBrowserControl ) + +wxMacDataBrowserListControl::wxMacDataBrowserListControl( wxWindow *peer, const wxPoint& pos, const wxSize& size, long style) + : wxMacDataItemBrowserControl( peer, pos, size, style ) +{ + OSStatus err = noErr; + m_clientDataItemsType = wxClientData_None; + if ( style & wxLB_SORT ) + m_sortOrder = SortOrder_Text_Ascending; + + DataBrowserSelectionFlags options = kDataBrowserDragSelect; + if ( style & wxLB_MULTIPLE ) + { + options |= kDataBrowserAlwaysExtendSelection | kDataBrowserCmdTogglesSelection; + } + else if ( style & wxLB_EXTENDED ) + { + options |= kDataBrowserCmdTogglesSelection; + } + else + { + options |= kDataBrowserSelectOnlyOne; + } + err = SetSelectionFlags( options ); + verify_noerr( err ); + + DataBrowserListViewColumnDesc columnDesc; + columnDesc.headerBtnDesc.titleOffset = 0; + columnDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc; + + columnDesc.headerBtnDesc.btnFontStyle.flags = + kControlUseFontMask | kControlUseJustMask; + + columnDesc.headerBtnDesc.btnContentInfo.contentType = kControlNoContent; + columnDesc.headerBtnDesc.btnFontStyle.just = teFlushDefault; + columnDesc.headerBtnDesc.btnFontStyle.font = kControlFontViewSystemFont; + columnDesc.headerBtnDesc.btnFontStyle.style = normal; + columnDesc.headerBtnDesc.titleString = NULL; + + columnDesc.headerBtnDesc.minimumWidth = 0; + columnDesc.headerBtnDesc.maximumWidth = 10000; + + columnDesc.propertyDesc.propertyID = kTextColumnId; + columnDesc.propertyDesc.propertyType = kDataBrowserTextType; + columnDesc.propertyDesc.propertyFlags = kDataBrowserTableViewSelectionColumn; + columnDesc.propertyDesc.propertyFlags |= kDataBrowserListViewTypeSelectColumn; + + verify_noerr( AddColumn( &columnDesc, kDataBrowserListViewAppendColumn ) ); + + columnDesc.headerBtnDesc.minimumWidth = 0; + columnDesc.headerBtnDesc.maximumWidth = 0; + columnDesc.propertyDesc.propertyID = kNumericOrderColumnId; + columnDesc.propertyDesc.propertyType = kDataBrowserPropertyRelevanceRankPart; + columnDesc.propertyDesc.propertyFlags = kDataBrowserTableViewSelectionColumn; + columnDesc.propertyDesc.propertyFlags |= kDataBrowserListViewTypeSelectColumn; + + verify_noerr( AddColumn( &columnDesc, kDataBrowserListViewAppendColumn ) ); + + SetDataBrowserSortProperty( m_controlRef , kTextColumnId); + if ( m_sortOrder == SortOrder_Text_Ascending ) + { + SetDataBrowserSortProperty( m_controlRef , kTextColumnId); + SetDataBrowserSortOrder( m_controlRef , kDataBrowserOrderIncreasing); + } + else + { + SetDataBrowserSortProperty( m_controlRef , kNumericOrderColumnId); + SetDataBrowserSortOrder( m_controlRef , kDataBrowserOrderIncreasing); + } + + verify_noerr( AutoSizeColumns() ); + verify_noerr( SetHiliteStyle(kDataBrowserTableViewFillHilite ) ); + verify_noerr( SetHeaderButtonHeight( 0 ) ); + err = SetHasScrollBars( (style & wxHSCROLL) != 0 , true ); +#if 0 + // shouldn't be necessary anymore under 10.2 + m_peer->SetData( kControlNoPart, kControlDataBrowserIncludesFrameAndFocusTag, (Boolean)false ); + m_peer->SetNeedsFocusRect( true ); +#endif +} + +wxMacDataBrowserListControl::~wxMacDataBrowserListControl() +{ +} + +void wxMacDataBrowserListControl::ItemNotification( + const wxMacDataItem* itemID, + DataBrowserItemNotification message, + DataBrowserItemDataRef itemData) +{ + wxListBox *list = wxDynamicCast( GetPeer() , wxListBox ); + wxCHECK_RET( list != NULL , wxT("Listbox expected")); + + if (list->HasMultipleSelection() && (message == kDataBrowserSelectionSetChanged) && (!list->MacGetBlockEvents())) + { + list->CalcAndSendEvent(); + return; + } + + if ((message == kDataBrowserSelectionSetChanged) && (!list->MacGetBlockEvents())) + { + wxCommandEvent event( wxEVT_COMMAND_LISTBOX_SELECTED, list->GetId() ); + + int sel = list->GetSelection(); + if ((sel < 0) || (sel > list->GetCount())) // OS X can select an item below the last item (why?) + return; + event.SetEventObject( list ); + if ( list->HasClientObjectData() ) + event.SetClientObject( list->GetClientObject( sel ) ); + else if ( list->HasClientUntypedData() ) + event.SetClientData( list->GetClientData( sel ) ); + event.SetString( list->GetString( sel ) ); + event.SetInt( sel ); + event.SetExtraLong( 1 ); + list->HandleWindowEvent(event); + return; + } + + // call super for item level(wxMacDataItem->Notification) callback processing + wxMacDataItemBrowserControl::ItemNotification( itemID, message, itemData); +} + +wxWindow * wxMacDataBrowserListControl::GetPeer() const +{ + return wxDynamicCast( wxMacControl::GetPeer() , wxWindow ); +} + +wxMacDataItem* wxMacDataBrowserListControl::CreateItem() +{ + return new wxMacListBoxItem(); +} + +#if 0 + +// in case we need that one day + +// ============================================================================ +// HIView owner-draw-based implementation +// ============================================================================ + +static pascal void ListBoxDrawProc( + ControlRef browser, DataBrowserItemID item, DataBrowserPropertyID property, + DataBrowserItemState itemState, const Rect *itemRect, SInt16 depth, Boolean isColorDevice ) +{ + CFStringRef cfString; + ThemeDrawingState themeState; + long systemVersion; + + GetThemeDrawingState( &themeState ); + cfString = CFStringCreateWithFormat( NULL, NULL, CFSTR("Row %d"), item ); + + // In this sample we handle the "selected" state; all others fall through to our "active" state + if ( itemState == kDataBrowserItemIsSelected ) + { + ThemeBrush colorBrushID; + + // TODO: switch over to wxSystemSettingsNative::GetColour() when kThemeBrushSecondaryHighlightColor + // is incorporated Panther DB starts using kThemeBrushSecondaryHighlightColor + // for inactive browser highlighting + if ( !IsControlActive( browser ) ) + colorBrushID = kThemeBrushSecondaryHighlightColor; + else + colorBrushID = kThemeBrushPrimaryHighlightColor; + + // First paint the hilite rect, then the text on top + SetThemePen( colorBrushID, 32, true ); + PaintRect( itemRect ); + SetThemeDrawingState( themeState, false ); + } + + DrawThemeTextBox( cfString, kThemeApplicationFont, kThemeStateActive, true, itemRect, teFlushDefault, NULL ); + SetThemeDrawingState( themeState, true ); + + if ( cfString != NULL ) + CFRelease( cfString ); +} + +#endif + + +#endif // wxUSE_LISTBOX diff --git a/src/osx/carbon/listctrl_mac.cpp b/src/osx/carbon/listctrl_mac.cpp new file mode 100644 index 0000000000..13a20ac220 --- /dev/null +++ b/src/osx/carbon/listctrl_mac.cpp @@ -0,0 +1,3382 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/listctrl_mac.cpp +// Purpose: wxListCtrl +// Author: Julian Smart +// Modified by: Agron Selimaj +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Julian Smart +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_LISTCTRL + +#include "wx/listctrl.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/settings.h" +#endif + +#include "wx/mac/uma.h" + +#include "wx/imaglist.h" +#include "wx/sysopt.h" +#include "wx/timer.h" + +#include "wx/hashmap.h" + +#if wxUSE_EXTENDED_RTTI +WX_DEFINE_FLAGS( wxListCtrlStyle ) + +wxBEGIN_FLAGS( wxListCtrlStyle ) + // new style border flags, we put them first to + // use them for streaming out + wxFLAGS_MEMBER(wxBORDER_SIMPLE) + wxFLAGS_MEMBER(wxBORDER_SUNKEN) + wxFLAGS_MEMBER(wxBORDER_DOUBLE) + wxFLAGS_MEMBER(wxBORDER_RAISED) + wxFLAGS_MEMBER(wxBORDER_STATIC) + wxFLAGS_MEMBER(wxBORDER_NONE) + + // old style border flags + wxFLAGS_MEMBER(wxSIMPLE_BORDER) + wxFLAGS_MEMBER(wxSUNKEN_BORDER) + wxFLAGS_MEMBER(wxDOUBLE_BORDER) + wxFLAGS_MEMBER(wxRAISED_BORDER) + wxFLAGS_MEMBER(wxSTATIC_BORDER) + wxFLAGS_MEMBER(wxBORDER) + + // standard window styles + wxFLAGS_MEMBER(wxTAB_TRAVERSAL) + wxFLAGS_MEMBER(wxCLIP_CHILDREN) + wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) + wxFLAGS_MEMBER(wxWANTS_CHARS) + wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) + wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) + wxFLAGS_MEMBER(wxVSCROLL) + wxFLAGS_MEMBER(wxHSCROLL) + + wxFLAGS_MEMBER(wxLC_LIST) + wxFLAGS_MEMBER(wxLC_REPORT) + wxFLAGS_MEMBER(wxLC_ICON) + wxFLAGS_MEMBER(wxLC_SMALL_ICON) + wxFLAGS_MEMBER(wxLC_ALIGN_TOP) + wxFLAGS_MEMBER(wxLC_ALIGN_LEFT) + wxFLAGS_MEMBER(wxLC_AUTOARRANGE) + wxFLAGS_MEMBER(wxLC_USER_TEXT) + wxFLAGS_MEMBER(wxLC_EDIT_LABELS) + wxFLAGS_MEMBER(wxLC_NO_HEADER) + wxFLAGS_MEMBER(wxLC_SINGLE_SEL) + wxFLAGS_MEMBER(wxLC_SORT_ASCENDING) + wxFLAGS_MEMBER(wxLC_SORT_DESCENDING) + wxFLAGS_MEMBER(wxLC_VIRTUAL) + +wxEND_FLAGS( wxListCtrlStyle ) + +IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl, wxControl,"wx/listctrl.h") + +wxBEGIN_PROPERTIES_TABLE(wxListCtrl) + wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent ) + + wxPROPERTY_FLAGS( WindowStyle , wxListCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style +wxEND_PROPERTIES_TABLE() + +wxBEGIN_HANDLERS_TABLE(wxListCtrl) +wxEND_HANDLERS_TABLE() + +wxCONSTRUCTOR_5( wxListCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle ) + +/* + TODO : Expose more information of a list's layout etc. via appropriate objects (ï¿  la NotebookPageInfo) +*/ +#else +IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl) +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl) +IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject) + +IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent) + +WX_DECLARE_HASH_MAP( int, wxListItem*, wxIntegerHash, wxIntegerEqual, wxListItemList ); + +#include "wx/listimpl.cpp" +WX_DEFINE_LIST(wxColumnList) + +// so we can check for column clicks +static const EventTypeSpec eventList[] = +{ + { kEventClassControl, kEventControlHit }, + { kEventClassControl, kEventControlDraw } +}; + +static pascal OSStatus wxMacListCtrlEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + + wxMacCarbonEvent cEvent( event ) ; + + ControlRef controlRef ; + cEvent.GetParameter( kEventParamDirectObject , &controlRef ) ; + + wxListCtrl *window = (wxListCtrl*) data ; + wxListEvent le( wxEVT_COMMAND_LIST_COL_CLICK, window->GetId() ); + le.SetEventObject( window ); + + switch ( GetEventKind( event ) ) + { + // check if the column was clicked on and fire an event if so + case kEventControlHit : + { + ControlPartCode result = cEvent.GetParameter(kEventParamControlPart, typeControlPartCode) ; + if (result == kControlButtonPart){ + DataBrowserPropertyID col; + GetDataBrowserSortProperty(controlRef, &col); + int column = col - kMinColumnId; + le.m_col = column; + // FIXME: we can't use the sort property for virtual listctrls + // so we need to find a better way to determine which column was clicked... + if (!window->IsVirtual()) + window->HandleWindowEvent( le ); + } + result = CallNextEventHandler(handler, event); + break; + } + case kEventControlDraw: + { + CGContextRef context = cEvent.GetParameter(kEventParamCGContextRef, typeCGContextRef) ; + window->MacSetDrawingContext(context); + result = CallNextEventHandler(handler, event); + window->MacSetDrawingContext(NULL); + break; + } + default : + break ; + } + + + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacListCtrlEventHandler ) + +class wxMacListCtrlItem : public wxMacListBoxItem +{ +public: + wxMacListCtrlItem(); + + virtual void Notification(wxMacDataItemBrowserControl *owner , + DataBrowserItemNotification message, + DataBrowserItemDataRef itemData ) const; + + virtual void SetColumnInfo( unsigned int column, wxListItem* item ); + virtual wxListItem* GetColumnInfo( unsigned int column ); + virtual bool HasColumnInfo( unsigned int column ); + + virtual void SetColumnTextValue( unsigned int column, const wxString& text ); + virtual wxString GetColumnTextValue( unsigned int column ); + + virtual int GetColumnImageValue( unsigned int column ); + virtual void SetColumnImageValue( unsigned int column, int imageIndex ); + + virtual ~wxMacListCtrlItem(); +protected: + wxListItemList m_rowItems; +}; + +DataBrowserDrawItemUPP gDataBrowserDrawItemUPP = NULL; +//DataBrowserEditItemUPP gDataBrowserEditItemUPP = NULL; +DataBrowserHitTestUPP gDataBrowserHitTestUPP = NULL; + +// TODO: Make a better name!! +class wxMacDataBrowserListCtrlControl : public wxMacDataItemBrowserControl +{ +public: + wxMacDataBrowserListCtrlControl( wxWindow *peer, const wxPoint& pos, const wxSize& size, long style ); + wxMacDataBrowserListCtrlControl() {} + virtual ~wxMacDataBrowserListCtrlControl(); + + // create a list item (can be a subclass of wxMacListBoxItem) + + virtual wxMacDataItem* CreateItem(); + + virtual void MacInsertItem( unsigned int n, wxListItem* item ); + virtual void MacSetColumnInfo( unsigned int row, unsigned int column, wxListItem* item ); + virtual void MacGetColumnInfo( unsigned int row, unsigned int column, wxListItem& item ); + virtual void UpdateState(wxMacDataItem* dataItem, wxListItem* item); + int GetFlags() { return m_flags; } + +protected: + // we need to override to provide specialized handling for virtual wxListCtrls + virtual OSStatus GetSetItemData(DataBrowserItemID itemID, + DataBrowserPropertyID property, + DataBrowserItemDataRef itemData, + Boolean changeValue ); + + virtual void ItemNotification( + DataBrowserItemID itemID, + DataBrowserItemNotification message, + DataBrowserItemDataRef itemData); + + virtual Boolean CompareItems(DataBrowserItemID itemOneID, + DataBrowserItemID itemTwoID, + DataBrowserPropertyID sortProperty); + + static pascal void DataBrowserDrawItemProc(ControlRef browser, + DataBrowserItemID item, + DataBrowserPropertyID property, + DataBrowserItemState itemState, + const Rect *theRect, + SInt16 gdDepth, + Boolean colorDevice); + + virtual void DrawItem(DataBrowserItemID itemID, + DataBrowserPropertyID property, + DataBrowserItemState itemState, + const Rect *itemRect, + SInt16 gdDepth, + Boolean colorDevice); + + static pascal Boolean DataBrowserEditTextProc(ControlRef browser, + DataBrowserItemID item, + DataBrowserPropertyID property, + CFStringRef theString, + Rect *maxEditTextRect, + Boolean *shrinkToFit); + + static pascal Boolean DataBrowserHitTestProc(ControlRef WXUNUSED(browser), + DataBrowserItemID WXUNUSED(itemID), + DataBrowserPropertyID WXUNUSED(property), + const Rect *WXUNUSED(theRect), + const Rect *WXUNUSED(mouseRect)) { return true; } + + virtual bool ConfirmEditText(DataBrowserItemID item, + DataBrowserPropertyID property, + CFStringRef theString, + Rect *maxEditTextRect, + Boolean *shrinkToFit); + + + + wxClientDataType m_clientDataItemsType; + bool m_isVirtual; + int m_flags; + DECLARE_DYNAMIC_CLASS_NO_COPY(wxMacDataBrowserListCtrlControl) +}; + +class wxMacListCtrlEventDelegate : public wxEvtHandler +{ +public: + wxMacListCtrlEventDelegate( wxListCtrl* list, int id ); + virtual bool ProcessEvent( wxEvent& event ); + +private: + wxListCtrl* m_list; + int m_id; +}; + +wxMacListCtrlEventDelegate::wxMacListCtrlEventDelegate( wxListCtrl* list, int id ) +{ + m_list = list; + m_id = id; +} + +bool wxMacListCtrlEventDelegate::ProcessEvent( wxEvent& event ) +{ + // even though we use a generic list ctrl underneath, make sure + // we present ourselves as wxListCtrl. + event.SetEventObject( m_list ); + event.SetId( m_id ); + + if ( !event.IsKindOf( CLASSINFO( wxCommandEvent ) ) ) + { + if (m_list->HandleWindowEvent( event )) + return true; + } + return wxEvtHandler::ProcessEvent(event); +} + +//----------------------------------------------------------------------------- +// wxListCtrlRenameTimer (internal) +//----------------------------------------------------------------------------- + +class wxListCtrlRenameTimer: public wxTimer +{ +private: + wxListCtrl *m_owner; + +public: + wxListCtrlRenameTimer( wxListCtrl *owner ); + void Notify(); +}; + +//----------------------------------------------------------------------------- +// wxListCtrlTextCtrlWrapper: wraps a wxTextCtrl to make it work for inline editing +//----------------------------------------------------------------------------- + +class wxListCtrlTextCtrlWrapper : public wxEvtHandler +{ +public: + // NB: text must be a valid object but not Create()d yet + wxListCtrlTextCtrlWrapper(wxListCtrl *owner, + wxTextCtrl *text, + long itemEdit); + + wxTextCtrl *GetText() const { return m_text; } + + void AcceptChangesAndFinish(); + +protected: + void OnChar( wxKeyEvent &event ); + void OnKeyUp( wxKeyEvent &event ); + void OnKillFocus( wxFocusEvent &event ); + + bool AcceptChanges(); + void Finish(); + +private: + wxListCtrl *m_owner; + wxTextCtrl *m_text; + wxString m_startValue; + long m_itemEdited; + bool m_finished; + bool m_aboutToFinish; + + DECLARE_EVENT_TABLE() +}; + +//----------------------------------------------------------------------------- +// wxListCtrlRenameTimer (internal) +//----------------------------------------------------------------------------- + +wxListCtrlRenameTimer::wxListCtrlRenameTimer( wxListCtrl *owner ) +{ + m_owner = owner; +} + +void wxListCtrlRenameTimer::Notify() +{ + m_owner->OnRenameTimer(); +} + +//----------------------------------------------------------------------------- +// wxListCtrlTextCtrlWrapper (internal) +//----------------------------------------------------------------------------- + +BEGIN_EVENT_TABLE(wxListCtrlTextCtrlWrapper, wxEvtHandler) + EVT_CHAR (wxListCtrlTextCtrlWrapper::OnChar) + EVT_KEY_UP (wxListCtrlTextCtrlWrapper::OnKeyUp) + EVT_KILL_FOCUS (wxListCtrlTextCtrlWrapper::OnKillFocus) +END_EVENT_TABLE() + +wxListCtrlTextCtrlWrapper::wxListCtrlTextCtrlWrapper(wxListCtrl *owner, + wxTextCtrl *text, + long itemEdit) + : m_startValue(owner->GetItemText(itemEdit)), + m_itemEdited(itemEdit) +{ + m_owner = owner; + m_text = text; + m_finished = false; + m_aboutToFinish = false; + + wxRect rectLabel; + int offset = 8; + owner->GetItemRect(itemEdit, rectLabel); + + m_text->Create(owner, wxID_ANY, m_startValue, + wxPoint(rectLabel.x+offset,rectLabel.y), + wxSize(rectLabel.width-offset,rectLabel.height)); + m_text->SetFocus(); + + m_text->PushEventHandler(this); +} + +void wxListCtrlTextCtrlWrapper::Finish() +{ + if ( !m_finished ) + { + m_finished = true; + + m_text->RemoveEventHandler(this); + m_owner->FinishEditing(m_text); + + wxPendingDelete.Append( this ); + } +} + +bool wxListCtrlTextCtrlWrapper::AcceptChanges() +{ + const wxString value = m_text->GetValue(); + + if ( value == m_startValue ) + // nothing changed, always accept + return true; + + if ( !m_owner->OnRenameAccept(m_itemEdited, value) ) + // vetoed by the user + return false; + + // accepted, do rename the item + m_owner->SetItemText(m_itemEdited, value); + + return true; +} + +void wxListCtrlTextCtrlWrapper::AcceptChangesAndFinish() +{ + m_aboutToFinish = true; + + // Notify the owner about the changes + AcceptChanges(); + + // Even if vetoed, close the control (consistent with MSW) + Finish(); +} + +void wxListCtrlTextCtrlWrapper::OnChar( wxKeyEvent &event ) +{ + switch ( event.m_keyCode ) + { + case WXK_RETURN: + AcceptChangesAndFinish(); + break; + + case WXK_ESCAPE: + m_owner->OnRenameCancelled( m_itemEdited ); + Finish(); + break; + + default: + event.Skip(); + } +} + +void wxListCtrlTextCtrlWrapper::OnKeyUp( wxKeyEvent &event ) +{ + if (m_finished) + { + event.Skip(); + return; + } + + // auto-grow the textctrl: + wxSize parentSize = m_owner->GetSize(); + wxPoint myPos = m_text->GetPosition(); + wxSize mySize = m_text->GetSize(); + int sx, sy; + m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy); + if (myPos.x + sx > parentSize.x) + sx = parentSize.x - myPos.x; + if (mySize.x > sx) + sx = mySize.x; + m_text->SetSize(sx, wxDefaultCoord); + + event.Skip(); +} + +void wxListCtrlTextCtrlWrapper::OnKillFocus( wxFocusEvent &event ) +{ + if ( !m_finished && !m_aboutToFinish ) + { + if ( !AcceptChanges() ) + m_owner->OnRenameCancelled( m_itemEdited ); + + Finish(); + } + + // We must let the native text control handle focus + event.Skip(); +} + +BEGIN_EVENT_TABLE(wxListCtrl, wxControl) + EVT_LEFT_DOWN(wxListCtrl::OnLeftDown) + EVT_LEFT_DCLICK(wxListCtrl::OnDblClick) + EVT_MIDDLE_DOWN(wxListCtrl::OnMiddleDown) + EVT_RIGHT_DOWN(wxListCtrl::OnRightDown) + EVT_CHAR(wxListCtrl::OnChar) +END_EVENT_TABLE() + +// ============================================================================ +// implementation +// ============================================================================ + +wxMacListControl* wxListCtrl::GetPeer() const +{ + wxMacDataBrowserListCtrlControl *lb = wxDynamicCast(m_peer,wxMacDataBrowserListCtrlControl); + return lb ? wx_static_cast(wxMacListControl*,lb) : 0 ; +} + +// ---------------------------------------------------------------------------- +// wxListCtrl construction +// ---------------------------------------------------------------------------- + +void wxListCtrl::Init() +{ + m_imageListNormal = NULL; + m_imageListSmall = NULL; + m_imageListState = NULL; + + // keep track of if we created our own image lists, or if they were assigned + // to us. + m_ownsImageListNormal = m_ownsImageListSmall = m_ownsImageListState = false; + m_colCount = 0; + m_count = 0; + m_textCtrl = NULL; + m_genericImpl = NULL; + m_dbImpl = NULL; + m_compareFunc = NULL; + m_compareFuncData = 0; + m_colsInfo = wxColumnList(); + m_textColor = wxNullColour; + m_bgColor = wxNullColour; + m_textctrlWrapper = NULL; + m_current = -1; + m_renameTimer = new wxListCtrlRenameTimer( this ); +} + +class wxGenericListCtrlHook : public wxGenericListCtrl +{ +public: + wxGenericListCtrlHook(wxListCtrl* parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) + : wxGenericListCtrl(parent, id, pos, size, style, validator, name), + m_nativeListCtrl(parent) + { + } + +protected: + virtual wxListItemAttr * OnGetItemAttr(long item) const + { + return m_nativeListCtrl->OnGetItemAttr(item); + } + + virtual int OnGetItemImage(long item) const + { + return m_nativeListCtrl->OnGetItemImage(item); + } + + virtual int OnGetItemColumnImage(long item, long column) const + { + return m_nativeListCtrl->OnGetItemColumnImage(item, column); + } + + virtual wxString OnGetItemText(long item, long column) const + { + return m_nativeListCtrl->OnGetItemText(item, column); + } + + wxListCtrl* m_nativeListCtrl; + +}; + +void wxListCtrl::OnLeftDown(wxMouseEvent& event) +{ + if ( m_textctrlWrapper ) + { + m_current = -1; + m_textctrlWrapper->AcceptChangesAndFinish(); + } + + int hitResult; + long current = HitTest(event.GetPosition(), hitResult); + if ((current == m_current) && + (hitResult == wxLIST_HITTEST_ONITEM) && + HasFlag(wxLC_EDIT_LABELS) ) + { + m_renameTimer->Start( 100, true ); + } + else + { + m_current = current; + } + event.Skip(); +} + +void wxListCtrl::OnDblClick(wxMouseEvent& event) +{ + m_current = -1; + event.Skip(); +} + +#if wxABI_VERSION >= 20801 +void wxListCtrl::OnRightDown(wxMouseEvent& event) +{ + if (m_dbImpl) + FireMouseEvent(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition()); + event.Skip(); +} + +void wxListCtrl::OnMiddleDown(wxMouseEvent& event) +{ + if (m_dbImpl) + FireMouseEvent(wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK, event.GetPosition()); + event.Skip(); +} + +void wxListCtrl::FireMouseEvent(wxEventType eventType, wxPoint position) +{ + wxListEvent le( eventType, GetId() ); + le.SetEventObject(this); + le.m_pointDrag = position; + le.m_itemIndex = -1; + + int flags; + long item = HitTest(position, flags); + if (flags & wxLIST_HITTEST_ONITEM) + { + le.m_itemIndex = item; + le.m_item.m_itemId = item; + GetItem(le.m_item); + HandleWindowEvent(le); + } +} + +void wxListCtrl::OnChar(wxKeyEvent& event) +{ + + + if (m_dbImpl) + { + wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, GetId() ); + le.SetEventObject(this); + le.m_code = event.GetKeyCode(); + le.m_itemIndex = -1; + + if (m_current == -1) + { + // if m_current isn't set, check if there's been a selection + // made before continuing + m_current = GetNextItem(-1, wxLIST_NEXT_BELOW, wxLIST_STATE_SELECTED); + } + + // We need to determine m_current ourselves when navigation keys + // are used. Note that PAGEUP and PAGEDOWN do not alter the current + // item on native Mac ListCtrl, so we only handle up and down keys. + switch ( event.GetKeyCode() ) + { + case WXK_UP: + if ( m_current > 0 ) + m_current -= 1; + else + m_current = 0; + + break; + + case WXK_DOWN: + if ( m_current < GetItemCount() - 1 ) + m_current += 1; + else + m_current = GetItemCount() - 1; + + break; + } + + if (m_current != -1) + { + le.m_itemIndex = m_current; + le.m_item.m_itemId = m_current; + GetItem(le.m_item); + HandleWindowEvent(le); + } + } + event.Skip(); +} +#endif + +bool wxListCtrl::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + + // for now, we'll always use the generic list control for ICON and LIST views, + // because they dynamically change the number of columns on resize. + // Also, allow the user to set it to use the list ctrl as well. + if ( (wxSystemOptions::HasOption( wxMAC_ALWAYS_USE_GENERIC_LISTCTRL ) + && (wxSystemOptions::GetOptionInt( wxMAC_ALWAYS_USE_GENERIC_LISTCTRL ) == 1)) || + (style & wxLC_ICON) || (style & wxLC_SMALL_ICON) || (style & wxLC_LIST) ) + { + m_macIsUserPane = true; + + long paneStyle = style; + paneStyle &= ~wxSIMPLE_BORDER; + paneStyle &= ~wxDOUBLE_BORDER; + paneStyle &= ~wxSUNKEN_BORDER; + paneStyle &= ~wxRAISED_BORDER; + paneStyle &= ~wxSTATIC_BORDER; + if ( !wxWindow::Create(parent, id, pos, size, paneStyle | wxNO_BORDER, name) ) + return false; + + // since the generic control is a child, make sure we position it at 0, 0 + m_genericImpl = new wxGenericListCtrlHook(this, id, wxPoint(0, 0), size, style, validator, name); + m_genericImpl->PushEventHandler( new wxMacListCtrlEventDelegate( this, GetId() ) ); + return true; + } + + else + { + m_macIsUserPane = false; + if ( !wxWindow::Create(parent, id, pos, size, style & ~(wxHSCROLL | wxVSCROLL), name) ) + return false; + m_dbImpl = new wxMacDataBrowserListCtrlControl( this, pos, size, style ); + m_peer = m_dbImpl; + + MacPostControlCreate( pos, size ); + + InstallControlEventHandler( m_peer->GetControlRef() , GetwxMacListCtrlEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, this, + (EventHandlerRef *)&m_macListCtrlEventHandler); + } + + return true; +} + +wxListCtrl::~wxListCtrl() +{ + if (m_genericImpl) + { + m_genericImpl->PopEventHandler(/* deleteHandler = */ true); + } + + if (m_ownsImageListNormal) + delete m_imageListNormal; + if (m_ownsImageListSmall) + delete m_imageListSmall; + if (m_ownsImageListState) + delete m_imageListState; + + delete m_renameTimer; + + WX_CLEAR_LIST(wxColumnList, m_colsInfo); +} + +/*static*/ +wxVisualAttributes +wxListCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) +{ + wxVisualAttributes attr; + + attr.colFg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ); + attr.colBg = wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX ); + attr.font.MacCreateFromThemeFont(kThemeViewsFont); + + return attr; +} + +// ---------------------------------------------------------------------------- +// set/get/change style +// ---------------------------------------------------------------------------- + +// Add or remove a single window style +void wxListCtrl::SetSingleStyle(long style, bool add) +{ + long flag = GetWindowStyleFlag(); + + // Get rid of conflicting styles + if ( add ) + { + if ( style & wxLC_MASK_TYPE) + flag = flag & ~wxLC_MASK_TYPE; + if ( style & wxLC_MASK_ALIGN ) + flag = flag & ~wxLC_MASK_ALIGN; + if ( style & wxLC_MASK_SORT ) + flag = flag & ~wxLC_MASK_SORT; + } + + if ( add ) + flag |= style; + else + flag &= ~style; + + SetWindowStyleFlag(flag); +} + +// Set the whole window style +void wxListCtrl::SetWindowStyleFlag(long flag) +{ + if ( flag != m_windowStyle ) + { + m_windowStyle = flag; + + if (m_genericImpl) + { + m_genericImpl->SetWindowStyleFlag(flag); + } + + Refresh(); + } +} + +void wxListCtrl::DoSetSize( int x, int y, int width, int height, int sizeFlags ) +{ + wxControl::DoSetSize(x, y, width, height, sizeFlags); + + if (m_genericImpl) + m_genericImpl->SetSize(0, 0, width, height, sizeFlags); + + // determine if we need a horizontal scrollbar, and add it if so + if (m_dbImpl) + { + int totalWidth = 0; + for (int column = 0; column < GetColumnCount(); column++) + { + totalWidth += m_dbImpl->GetColumnWidth( column ); + } + + if ( !(m_dbImpl->GetFlags() & wxHSCROLL) ) + { + Boolean vertScrollBar; + GetDataBrowserHasScrollBars( m_dbImpl->GetControlRef(), NULL, &vertScrollBar ); + if (totalWidth > width) + SetDataBrowserHasScrollBars( m_dbImpl->GetControlRef(), true, vertScrollBar ); + else + SetDataBrowserHasScrollBars( m_dbImpl->GetControlRef(), false, vertScrollBar ); + } + } +} + +wxSize wxListCtrl::DoGetBestSize() const +{ + return wxWindow::DoGetBestSize(); +} + +bool wxListCtrl::SetFont(const wxFont& font) +{ + bool rv = true; + rv = wxControl::SetFont(font); + if (m_genericImpl) + rv = m_genericImpl->SetFont(font); + return rv; +} + +bool wxListCtrl::SetForegroundColour(const wxColour& colour) +{ + bool rv = true; + if (m_genericImpl) + rv = m_genericImpl->SetForegroundColour(colour); + if (m_dbImpl) + SetTextColour(colour); + return rv; +} + +bool wxListCtrl::SetBackgroundColour(const wxColour& colour) +{ + bool rv = true; + if (m_genericImpl) + rv = m_genericImpl->SetBackgroundColour(colour); + if (m_dbImpl) + m_bgColor = colour; + return rv; +} + +wxColour wxListCtrl::GetBackgroundColour() +{ + if (m_genericImpl) + return m_genericImpl->GetBackgroundColour(); + if (m_dbImpl) + return m_bgColor; + + return wxNullColour; +} + +// ---------------------------------------------------------------------------- +// accessors +// ---------------------------------------------------------------------------- + +// Gets information about this column +bool wxListCtrl::GetColumn(int col, wxListItem& item) const +{ + if (m_genericImpl) + return m_genericImpl->GetColumn(col, item); + + bool success = true; + + if (m_dbImpl) + { + wxColumnList::compatibility_iterator node = m_colsInfo.Item( col ); + wxASSERT_MSG( node, _T("invalid column index in wxMacListCtrlItem") ); + wxListItem* column = node->GetData(); + + long mask = column->GetMask(); + if (mask & wxLIST_MASK_TEXT) + item.SetText(column->GetText()); + if (mask & wxLIST_MASK_DATA) + item.SetData(column->GetData()); + if (mask & wxLIST_MASK_IMAGE) + item.SetImage(column->GetImage()); + if (mask & wxLIST_MASK_STATE) + item.SetState(column->GetState()); + if (mask & wxLIST_MASK_FORMAT) + item.SetAlign(column->GetAlign()); + if (mask & wxLIST_MASK_WIDTH) + item.SetWidth(column->GetWidth()); + } + + return success; +} + +// Sets information about this column +bool wxListCtrl::SetColumn(int col, wxListItem& item) +{ + if (m_genericImpl) + return m_genericImpl->SetColumn(col, item); + + if (m_dbImpl) + { + long mask = item.GetMask(); + if ( col >= (int)m_colsInfo.GetCount() ) + { + wxListItem* listItem = new wxListItem(item); + m_colsInfo.Append( listItem ); + } + else + { + wxListItem listItem; + GetColumn( col, listItem ); + + if (mask & wxLIST_MASK_TEXT) + listItem.SetText(item.GetText()); + if (mask & wxLIST_MASK_DATA) + listItem.SetData(item.GetData()); + if (mask & wxLIST_MASK_IMAGE) + listItem.SetImage(item.GetImage()); + if (mask & wxLIST_MASK_STATE) + listItem.SetState(item.GetState()); + if (mask & wxLIST_MASK_FORMAT) + listItem.SetAlign(item.GetAlign()); + if (mask & wxLIST_MASK_WIDTH) + listItem.SetWidth(item.GetWidth()); + } + + // change the appearance in the databrowser. + DataBrowserListViewHeaderDesc columnDesc; + columnDesc.version=kDataBrowserListViewLatestHeaderDesc; + verify_noerr( m_dbImpl->GetHeaderDesc( kMinColumnId + col, &columnDesc ) ); + + /* + if (item.GetMask() & wxLIST_MASK_TEXT) + { + wxFontEncoding enc; + if ( m_font.Ok() ) + enc = GetFont().GetEncoding(); + else + enc = wxLocale::GetSystemEncoding(); + wxCFStringRef cfTitle; + cfTitle.Assign( item.GetText() , enc ); + if(columnDesc.titleString) + CFRelease(columnDesc.titleString); + columnDesc.titleString = cfTitle; + } + */ + + if (item.GetMask() & wxLIST_MASK_IMAGE && item.GetImage() != -1 ) + { + wxImageList* imageList = GetImageList(wxIMAGE_LIST_SMALL); + if (imageList && imageList->GetImageCount() > 0 ) + { + wxBitmap bmp = imageList->GetBitmap( item.GetImage() ); + IconRef icon = bmp.GetIconRef(); + columnDesc.btnContentInfo.u.iconRef = icon; + columnDesc.btnContentInfo.contentType = kControlContentIconRef; + } + } + + verify_noerr( m_dbImpl->SetHeaderDesc( kMinColumnId + col, &columnDesc ) ); + + } + return true; +} + +int wxListCtrl::GetColumnCount() const +{ + if (m_genericImpl) + return m_genericImpl->GetColumnCount(); + + if (m_dbImpl) + { + UInt32 count; + m_dbImpl->GetColumnCount(&count); + return count; + } + + return m_colCount; +} + +// Gets the column width +int wxListCtrl::GetColumnWidth(int col) const +{ + if (m_genericImpl) + return m_genericImpl->GetColumnWidth(col); + + if (m_dbImpl) + { + return m_dbImpl->GetColumnWidth(col); + } + + return 0; +} + +// Sets the column width +bool wxListCtrl::SetColumnWidth(int col, int width) +{ + if (m_genericImpl) + return m_genericImpl->SetColumnWidth(col, width); + + if (m_dbImpl) + { + if ( width == wxLIST_AUTOSIZE_USEHEADER ) + { + width = 150; // FIXME + } + + if (col == -1) + { + for (int column = 0; column < GetColumnCount(); column++) + { + wxListItem colInfo; + GetColumn(column, colInfo); + + colInfo.SetWidth(width); + SetColumn(column, colInfo); + + const int mywidth = (width == wxLIST_AUTOSIZE) + ? CalcColumnAutoWidth(column) : width; + m_dbImpl->SetColumnWidth(column, mywidth); + } + } + else + { + if ( width == wxLIST_AUTOSIZE ) + width = CalcColumnAutoWidth(col); + + wxListItem colInfo; + GetColumn(col, colInfo); + + colInfo.SetWidth(width); + SetColumn(col, colInfo); + m_dbImpl->SetColumnWidth(col, width); + } + return true; + } + + return false; +} + +// Gets the number of items that can fit vertically in the +// visible area of the list control (list or report view) +// or the total number of items in the list control (icon +// or small icon view) +int wxListCtrl::GetCountPerPage() const +{ + if (m_genericImpl) + return m_genericImpl->GetCountPerPage(); + + if (m_dbImpl) + { + UInt16 height = 1; + m_dbImpl->GetDefaultRowHeight( &height ); + if (height > 0) + return GetClientSize().y / height; + } + + return 1; +} + +// Gets the edit control for editing labels. +wxTextCtrl* wxListCtrl::GetEditControl() const +{ + if (m_genericImpl) + return m_genericImpl->GetEditControl(); + + return NULL; +} + +// Gets information about the item +bool wxListCtrl::GetItem(wxListItem& info) const +{ + if (m_genericImpl) + return m_genericImpl->GetItem(info); + + if (m_dbImpl) + { + if (!IsVirtual()) + { + if (info.m_itemId >= 0 && info.m_itemId < GetItemCount()) + { + m_dbImpl->MacGetColumnInfo(info.m_itemId, info.m_col, info); + if (info.GetMask() & wxLIST_MASK_STATE) + { + DataBrowserItemID id = (DataBrowserItemID)m_dbImpl->GetItemFromLine(info.m_itemId); + if (IsDataBrowserItemSelected( m_dbImpl->GetControlRef(), id )) + info.SetState(info.GetState() | wxLIST_STATE_SELECTED); + } + } + } + else + { + if (info.m_itemId >= 0 && info.m_itemId < GetItemCount()) + { + info.SetText( OnGetItemText(info.m_itemId, info.m_col) ); + info.SetImage( OnGetItemColumnImage(info.m_itemId, info.m_col) ); + if (info.GetMask() & wxLIST_MASK_STATE) + { + if (IsDataBrowserItemSelected( m_dbImpl->GetControlRef(), info.m_itemId+1 )) + info.SetState(info.GetState() | wxLIST_STATE_SELECTED); + } + + wxListItemAttr* attrs = OnGetItemAttr( info.m_itemId ); + if (attrs) + { + info.SetFont( attrs->GetFont() ); + info.SetBackgroundColour( attrs->GetBackgroundColour() ); + info.SetTextColour( attrs->GetTextColour() ); + } + } + } + } + bool success = true; + return success; +} + +// Sets information about the item +bool wxListCtrl::SetItem(wxListItem& info) +{ + if (m_genericImpl) + return m_genericImpl->SetItem(info); + + if (m_dbImpl) + m_dbImpl->MacSetColumnInfo( info.m_itemId, info.m_col, &info ); + + return true; +} + +long wxListCtrl::SetItem(long index, int col, const wxString& label, int imageId) +{ + if (m_genericImpl) + return m_genericImpl->SetItem(index, col, label, imageId); + + wxListItem info; + info.m_text = label; + info.m_mask = wxLIST_MASK_TEXT; + info.m_itemId = index; + info.m_col = col; + if ( imageId > -1 ) + { + info.m_image = imageId; + info.m_mask |= wxLIST_MASK_IMAGE; + } + return SetItem(info); +} + + +// Gets the item state +int wxListCtrl::GetItemState(long item, long stateMask) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemState(item, stateMask); + + if (m_dbImpl) + { + if ( HasFlag(wxLC_VIRTUAL) ) + { + if (stateMask == wxLIST_STATE_SELECTED) + { + if (IsDataBrowserItemSelected( m_dbImpl->GetControlRef(), item+1 )) + return wxLIST_STATE_SELECTED; + else + return 0; + } + } + else + { + wxListItem info; + + info.m_mask = wxLIST_MASK_STATE; + info.m_stateMask = stateMask; + info.m_itemId = item; + + if (!GetItem(info)) + return 0; + + return info.m_state; + } + } + + return 0; +} + +// Sets the item state +bool wxListCtrl::SetItemState(long item, long state, long stateMask) +{ + if (m_genericImpl) + return m_genericImpl->SetItemState(item, state, stateMask); + + if (m_dbImpl) + { + DataBrowserSetOption option = kDataBrowserItemsAdd; + if ( (stateMask & wxLIST_STATE_SELECTED) && state == 0 ) + option = kDataBrowserItemsRemove; + + if (item == -1) + { + if ( HasFlag(wxLC_VIRTUAL) ) + { + wxMacDataItemBrowserSelectionSuppressor suppressor(m_dbImpl); + m_dbImpl->SetSelectedAllItems(option); + } + else + { + for(int i = 0; i < GetItemCount();i++) + { + wxListItem info; + info.m_itemId = i; + info.m_mask = wxLIST_MASK_STATE; + info.m_stateMask = stateMask; + info.m_state = state; + SetItem(info); + } + } + } + else + { + if ( HasFlag(wxLC_VIRTUAL) ) + { + long itemID = item+1; + bool isSelected = IsDataBrowserItemSelected(m_dbImpl->GetControlRef(), (DataBrowserItemID)itemID ); + bool isSelectedState = (state == wxLIST_STATE_SELECTED); + + // toggle the selection state if wxListInfo state and actual state don't match. + if ( (stateMask & wxLIST_STATE_SELECTED) && isSelected != isSelectedState ) + { + SetDataBrowserSelectedItems(m_dbImpl->GetControlRef(), 1, (DataBrowserItemID*)&itemID, option); + } + } + else + { + wxListItem info; + info.m_itemId = item; + info.m_mask = wxLIST_MASK_STATE; + info.m_stateMask = stateMask; + info.m_state = state; + return SetItem(info); + } + } + } + return true; +} + +// Sets the item image +bool wxListCtrl::SetItemImage(long item, int image, int WXUNUSED(selImage)) +{ + return SetItemColumnImage(item, 0, image); +} + +// Sets the item image +bool wxListCtrl::SetItemColumnImage(long item, long column, int image) +{ + if (m_genericImpl) + return m_genericImpl->SetItemColumnImage(item, column, image); + + wxListItem info; + + info.m_mask = wxLIST_MASK_IMAGE; + info.m_image = image; + info.m_itemId = item; + info.m_col = column; + + return SetItem(info); +} + +// Gets the item text +wxString wxListCtrl::GetItemText(long item) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemText(item); + + wxListItem info; + + info.m_mask = wxLIST_MASK_TEXT; + info.m_itemId = item; + + if (!GetItem(info)) + return wxEmptyString; + return info.m_text; +} + +// Sets the item text +void wxListCtrl::SetItemText(long item, const wxString& str) +{ + if (m_genericImpl) + return m_genericImpl->SetItemText(item, str); + + wxListItem info; + + info.m_mask = wxLIST_MASK_TEXT; + info.m_itemId = item; + info.m_text = str; + + SetItem(info); +} + +// Gets the item data +long wxListCtrl::GetItemData(long item) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemData(item); + + wxListItem info; + + info.m_mask = wxLIST_MASK_DATA; + info.m_itemId = item; + + if (!GetItem(info)) + return 0; + return info.m_data; +} + +// Sets the item data +bool wxListCtrl::SetItemPtrData(long item, wxUIntPtr data) +{ + if (m_genericImpl) + return m_genericImpl->SetItemData(item, data); + + wxListItem info; + + info.m_mask = wxLIST_MASK_DATA; + info.m_itemId = item; + info.m_data = data; + + return SetItem(info); +} + +wxRect wxListCtrl::GetViewRect() const +{ + wxASSERT_MSG( !HasFlag(wxLC_REPORT | wxLC_LIST), + _T("wxListCtrl::GetViewRect() only works in icon mode") ); + + if (m_genericImpl) + return m_genericImpl->GetViewRect(); + + wxRect rect; + return rect; +} + +// Gets the item rectangle +bool wxListCtrl::GetItemRect(long item, wxRect& rect, int code) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemRect(item, rect, code); + + + if (m_dbImpl) + { + DataBrowserItemID id; + DataBrowserPropertyID col = kMinColumnId; + Rect bounds; + DataBrowserPropertyPart part = kDataBrowserPropertyEnclosingPart; + if ( code == wxLIST_RECT_LABEL ) + part = kDataBrowserPropertyTextPart; + else if ( code == wxLIST_RECT_ICON ) + part = kDataBrowserPropertyIconPart; + + if ( !(GetWindowStyleFlag() & wxLC_VIRTUAL) ) + { + wxMacDataItem* thisItem = m_dbImpl->GetItemFromLine(item); + id = (DataBrowserItemID) thisItem; + } + else + id = item+1; + + GetDataBrowserItemPartBounds( m_dbImpl->GetControlRef(), id, col, part, &bounds ); + + rect.x = bounds.left; + rect.y = bounds.top; + rect.width = bounds.right - bounds.left; //GetClientSize().x; // we need the width of the whole row, not just the item. + rect.height = bounds.bottom - bounds.top; + //fprintf("id = %d, bounds = %d, %d, %d, %d\n", id, rect.x, rect.y, rect.width, rect.height); + } + return true; +} + +// Gets the item position +bool wxListCtrl::GetItemPosition(long item, wxPoint& pos) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemPosition(item, pos); + + bool success = false; + + if (m_dbImpl) + { + wxRect itemRect; + GetItemRect(item, itemRect); + pos = itemRect.GetPosition(); + success = true; + } + + return success; +} + +// Sets the item position. +bool wxListCtrl::SetItemPosition(long item, const wxPoint& pos) +{ + if (m_genericImpl) + return m_genericImpl->SetItemPosition(item, pos); + + return false; +} + +// Gets the number of items in the list control +int wxListCtrl::GetItemCount() const +{ + if (m_genericImpl) + return m_genericImpl->GetItemCount(); + + if (m_dbImpl) + return m_dbImpl->MacGetCount(); + + return m_count; +} + +void wxListCtrl::SetItemSpacing( int spacing, bool isSmall ) +{ + if (m_genericImpl) + m_genericImpl->SetItemSpacing(spacing, isSmall); +} + +wxSize wxListCtrl::GetItemSpacing() const +{ + if (m_genericImpl) + return m_genericImpl->GetItemSpacing(); + + return wxSize(0, 0); +} + +void wxListCtrl::SetItemTextColour( long item, const wxColour &col ) +{ + if (m_genericImpl) + { + m_genericImpl->SetItemTextColour(item, col); + return; + } + + wxListItem info; + info.m_itemId = item; + info.SetTextColour( col ); + SetItem( info ); +} + +wxColour wxListCtrl::GetItemTextColour( long item ) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemTextColour(item); + + if (m_dbImpl) + { + wxListItem info; + if (GetItem(info)) + return info.GetTextColour(); + } + return wxNullColour; +} + +void wxListCtrl::SetItemBackgroundColour( long item, const wxColour &col ) +{ + if (m_genericImpl) + { + m_genericImpl->SetItemBackgroundColour(item, col); + return; + } + + wxListItem info; + info.m_itemId = item; + info.SetBackgroundColour( col ); + SetItem( info ); +} + +wxColour wxListCtrl::GetItemBackgroundColour( long item ) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemBackgroundColour(item); + + if (m_dbImpl) + { + wxListItem info; + if (GetItem(info)) + return info.GetBackgroundColour(); + } + return wxNullColour; +} + +void wxListCtrl::SetItemFont( long item, const wxFont &f ) +{ + if (m_genericImpl) + { + m_genericImpl->SetItemFont(item, f); + return; + } + + wxListItem info; + info.m_itemId = item; + info.SetFont( f ); + SetItem( info ); +} + +wxFont wxListCtrl::GetItemFont( long item ) const +{ + if (m_genericImpl) + return m_genericImpl->GetItemFont(item); + + if (m_dbImpl) + { + wxListItem info; + if (GetItem(info)) + return info.GetFont(); + } + + return wxNullFont; +} + +// Gets the number of selected items in the list control +int wxListCtrl::GetSelectedItemCount() const +{ + if (m_genericImpl) + return m_genericImpl->GetSelectedItemCount(); + + if (m_dbImpl) + return m_dbImpl->GetSelectedItemCount(NULL, true); + + return 0; +} + +// Gets the text colour of the listview +wxColour wxListCtrl::GetTextColour() const +{ + if (m_genericImpl) + return m_genericImpl->GetTextColour(); + + // TODO: we need owner drawn list items to customize text color. + if (m_dbImpl) + return m_textColor; + + return wxNullColour; +} + +// Sets the text colour of the listview +void wxListCtrl::SetTextColour(const wxColour& col) +{ + if (m_genericImpl) + { + m_genericImpl->SetTextColour(col); + return; + } + + if (m_dbImpl) + m_textColor = col; +} + +// Gets the index of the topmost visible item when in +// list or report view +long wxListCtrl::GetTopItem() const +{ + if (m_genericImpl) + return m_genericImpl->GetTopItem(); + + if (m_dbImpl) + { + int flags = 0; + long item = HitTest( wxPoint(1, 1), flags); + if (flags == wxLIST_HITTEST_ONITEM) + return item; + } + + return 0; +} + +// Searches for an item, starting from 'item'. +// 'geometry' is one of +// wxLIST_NEXT_ABOVE/ALL/BELOW/LEFT/RIGHT. +// 'state' is a state bit flag, one or more of +// wxLIST_STATE_DROPHILITED/FOCUSED/SELECTED/CUT. +// item can be -1 to find the first item that matches the +// specified flags. +// Returns the item or -1 if unsuccessful. +long wxListCtrl::GetNextItem(long item, int geom, int state) const +{ + if (m_genericImpl) + return m_genericImpl->GetNextItem(item, geom, state); + + // TODO: implement all geometry and state options? + if ( m_dbImpl ) + { + if ( geom == wxLIST_NEXT_ALL || geom == wxLIST_NEXT_BELOW ) + { + long count = m_dbImpl->MacGetCount() ; + for ( long line = item + 1 ; line < count; line++ ) + { + DataBrowserItemID id = line + 1; + if ( !IsVirtual() ) + id = (DataBrowserItemID)m_dbImpl->GetItemFromLine(line); + + if ( (state == wxLIST_STATE_DONTCARE ) ) + return line; + + if ( (state & wxLIST_STATE_SELECTED) && IsDataBrowserItemSelected(m_dbImpl->GetControlRef(), id ) ) + return line; + } + } + + if ( geom == wxLIST_NEXT_ABOVE ) + { + int item2 = item; + if ( item2 == -1 ) + item2 = m_dbImpl->MacGetCount(); + + for ( long line = item2 - 1 ; line >= 0; line-- ) + { + DataBrowserItemID id = line + 1; + if ( !IsVirtual() ) + id = (DataBrowserItemID)m_dbImpl->GetItemFromLine(line); + + if ( (state == wxLIST_STATE_DONTCARE ) ) + return line; + + if ( (state & wxLIST_STATE_SELECTED) && IsDataBrowserItemSelected(m_dbImpl->GetControlRef(), id ) ) + return line; + } + } + } + + return -1; +} + + +wxImageList *wxListCtrl::GetImageList(int which) const +{ + if (m_genericImpl) + return m_genericImpl->GetImageList(which); + + if ( which == wxIMAGE_LIST_NORMAL ) + { + return m_imageListNormal; + } + else if ( which == wxIMAGE_LIST_SMALL ) + { + return m_imageListSmall; + } + else if ( which == wxIMAGE_LIST_STATE ) + { + return m_imageListState; + } + return NULL; +} + +void wxListCtrl::SetImageList(wxImageList *imageList, int which) +{ + if (m_genericImpl) + { + m_genericImpl->SetImageList(imageList, which); + return; + } + + if ( which == wxIMAGE_LIST_NORMAL ) + { + if (m_ownsImageListNormal) delete m_imageListNormal; + m_imageListNormal = imageList; + m_ownsImageListNormal = false; + } + else if ( which == wxIMAGE_LIST_SMALL ) + { + if (m_ownsImageListSmall) delete m_imageListSmall; + m_imageListSmall = imageList; + m_ownsImageListSmall = false; + } + else if ( which == wxIMAGE_LIST_STATE ) + { + if (m_ownsImageListState) delete m_imageListState; + m_imageListState = imageList; + m_ownsImageListState = false; + } +} + +void wxListCtrl::AssignImageList(wxImageList *imageList, int which) +{ + if (m_genericImpl) + { + m_genericImpl->AssignImageList(imageList, which); + return; + } + + SetImageList(imageList, which); + if ( which == wxIMAGE_LIST_NORMAL ) + m_ownsImageListNormal = true; + else if ( which == wxIMAGE_LIST_SMALL ) + m_ownsImageListSmall = true; + else if ( which == wxIMAGE_LIST_STATE ) + m_ownsImageListState = true; +} + +// ---------------------------------------------------------------------------- +// Operations +// ---------------------------------------------------------------------------- + +// Arranges the items +bool wxListCtrl::Arrange(int flag) +{ + if (m_genericImpl) + return m_genericImpl->Arrange(flag); + return false; +} + +// Deletes an item +bool wxListCtrl::DeleteItem(long item) +{ + if (m_genericImpl) + return m_genericImpl->DeleteItem(item); + + if (m_dbImpl) + { + m_dbImpl->MacDelete(item); + wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ITEM, GetId() ); + event.SetEventObject( this ); + event.m_itemIndex = item; + HandleWindowEvent( event ); + + } + return true; +} + +// Deletes all items +bool wxListCtrl::DeleteAllItems() +{ + if (m_genericImpl) + return m_genericImpl->DeleteAllItems(); + + if (m_dbImpl) + { + m_dbImpl->MacClear(); + wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, GetId() ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + } + return true; +} + +// Deletes all items +bool wxListCtrl::DeleteAllColumns() +{ + if (m_genericImpl) + return m_genericImpl->DeleteAllColumns(); + + if (m_dbImpl) + { + UInt32 cols; + m_dbImpl->GetColumnCount(&cols); + for (UInt32 col = 0; col < cols; col++) + { + DeleteColumn(0); + } + } + + return true; +} + +// Deletes a column +bool wxListCtrl::DeleteColumn(int col) +{ + if (m_genericImpl) + return m_genericImpl->DeleteColumn(col); + + if (m_dbImpl) + { + OSStatus err = m_dbImpl->RemoveColumn(col); + return err == noErr; + } + + return true; +} + +// Clears items, and columns if there are any. +void wxListCtrl::ClearAll() +{ + if (m_genericImpl) + { + m_genericImpl->ClearAll(); + return; + } + + if (m_dbImpl) + { + DeleteAllItems(); + DeleteAllColumns(); + } +} + +wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass) +{ + if (m_genericImpl) + return m_genericImpl->EditLabel(item, textControlClass); + + if (m_dbImpl) + { + wxCHECK_MSG( (item >= 0) && ((long)item < GetItemCount()), NULL, + wxT("wrong index in wxListCtrl::EditLabel()") ); + + wxASSERT_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)), + wxT("EditLabel() needs a text control") ); + + wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() ); + le.SetEventObject( this ); + le.m_itemIndex = item; + le.m_col = 0; + GetItem( le.m_item ); + + if ( GetParent()->HandleWindowEvent( le ) && !le.IsAllowed() ) + { + // vetoed by user code + return NULL; + } + + wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject(); + m_textctrlWrapper = new wxListCtrlTextCtrlWrapper(this, text, item); + return m_textctrlWrapper->GetText(); + } + return NULL; +} + +// End label editing, optionally cancelling the edit +bool wxListCtrl::EndEditLabel(bool WXUNUSED(cancel)) +{ + // TODO: generic impl. doesn't have this method - is it needed for us? + if (m_genericImpl) + return true; // m_genericImpl->EndEditLabel(cancel); + + if (m_dbImpl) + verify_noerr( SetDataBrowserEditItem(m_dbImpl->GetControlRef(), kDataBrowserNoItem, kMinColumnId) ); + return true; +} + +// Ensures this item is visible +bool wxListCtrl::EnsureVisible(long item) +{ + if (m_genericImpl) + return m_genericImpl->EnsureVisible(item); + + if (m_dbImpl) + { + wxMacDataItem* dataItem = m_dbImpl->GetItemFromLine(item); + m_dbImpl->RevealItem(dataItem, kDataBrowserRevealWithoutSelecting); + } + + return true; +} + +// Find an item whose label matches this string, starting from the item after 'start' +// or the beginning if 'start' is -1. +long wxListCtrl::FindItem(long start, const wxString& str, bool partial) +{ + if (m_genericImpl) + return m_genericImpl->FindItem(start, str, partial); + + wxString str_upper = str.Upper(); + + long idx = start; + if (idx < 0) + idx = 0; + long count = GetItemCount(); + + while (idx < count) + { + wxString line_upper = GetItemText(idx).Upper(); + if (!partial) + { + if (line_upper == str_upper ) + return idx; + } + else + { + if (line_upper.find(str_upper) == 0) + return idx; + } + + idx++; + }; + + return wxNOT_FOUND; +} + +// Find an item whose data matches this data, starting from the item after 'start' +// or the beginning if 'start' is -1. +long wxListCtrl::FindItem(long start, long data) +{ + if (m_genericImpl) + return m_genericImpl->FindItem(start, data); + + long idx = start; + if (idx < 0) + idx = 0; + long count = GetItemCount(); + + while (idx < count) + { + if (GetItemData(idx) == data) + return idx; + idx++; + }; + + return wxNOT_FOUND; +} + +// Find an item nearest this position in the specified direction, starting from +// the item after 'start' or the beginning if 'start' is -1. +long wxListCtrl::FindItem(long start, const wxPoint& pt, int direction) +{ + if (m_genericImpl) + return m_genericImpl->FindItem(start, pt, direction); + return -1; +} + +// Determines which item (if any) is at the specified point, +// giving details in 'flags' (see wxLIST_HITTEST_... flags above) +long +wxListCtrl::HitTest(const wxPoint& point, int& flags, long *ptrSubItem) const +{ + if (m_genericImpl) + return m_genericImpl->HitTest(point, flags, ptrSubItem); + + flags = wxLIST_HITTEST_NOWHERE; + if (m_dbImpl) + { + int colHeaderHeight = 22; // TODO: Find a way to get this value from the db control? + UInt16 rowHeight = 0; + m_dbImpl->GetDefaultRowHeight(&rowHeight); + + int y = point.y; + // get the actual row by taking scroll position into account + UInt32 offsetX, offsetY; + m_dbImpl->GetScrollPosition( &offsetY, &offsetX ); + y += offsetY; + + if ( !(GetWindowStyleFlag() & wxLC_NO_HEADER) ) + y -= colHeaderHeight; + + if ( y < 0 ) + return -1; + + int row = y / rowHeight; + DataBrowserItemID id; + m_dbImpl->GetItemID( (DataBrowserTableViewRowIndex) row, &id ); + + // TODO: Use GetDataBrowserItemPartBounds to return if we are in icon or label + if ( !(GetWindowStyleFlag() & wxLC_VIRTUAL ) ) + { + wxMacListCtrlItem* lcItem; + lcItem = (wxMacListCtrlItem*) id; + if (lcItem) + { + flags = wxLIST_HITTEST_ONITEM; + return row; + } + } + else + { + if (row < GetItemCount() ) + { + flags = wxLIST_HITTEST_ONITEM; + return row; + } + } + + } + return -1; +} + +int wxListCtrl::GetScrollPos(int orient) const +{ + if (m_genericImpl) + return m_genericImpl->GetScrollPos(orient); + + if (m_dbImpl) + { + UInt32 offsetX, offsetY; + m_dbImpl->GetScrollPosition( &offsetY, &offsetX ); + if ( orient == wxHORIZONTAL ) + return offsetX; + else + return offsetY; + } + + return 0; +} + +// Inserts an item, returning the index of the new item if successful, +// -1 otherwise. +long wxListCtrl::InsertItem(wxListItem& info) +{ + wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual controls") ); + + if (m_genericImpl) + return m_genericImpl->InsertItem(info); + + if (m_dbImpl && !IsVirtual()) + { + int count = GetItemCount(); + + if (info.m_itemId > count) + info.m_itemId = count; + + m_dbImpl->MacInsertItem(info.m_itemId, &info ); + + wxListEvent event( wxEVT_COMMAND_LIST_INSERT_ITEM, GetId() ); + event.SetEventObject( this ); + event.m_itemIndex = info.m_itemId; + HandleWindowEvent( event ); + return info.m_itemId; + } + return -1; +} + +long wxListCtrl::InsertItem(long index, const wxString& label) +{ + if (m_genericImpl) + return m_genericImpl->InsertItem(index, label); + + wxListItem info; + info.m_text = label; + info.m_mask = wxLIST_MASK_TEXT; + info.m_itemId = index; + return InsertItem(info); +} + +// Inserts an image item +long wxListCtrl::InsertItem(long index, int imageIndex) +{ + if (m_genericImpl) + return m_genericImpl->InsertItem(index, imageIndex); + + wxListItem info; + info.m_image = imageIndex; + info.m_mask = wxLIST_MASK_IMAGE; + info.m_itemId = index; + return InsertItem(info); +} + +// Inserts an image/string item +long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex) +{ + if (m_genericImpl) + return m_genericImpl->InsertItem(index, label, imageIndex); + + wxListItem info; + info.m_image = imageIndex; + info.m_text = label; + info.m_mask = wxLIST_MASK_IMAGE | wxLIST_MASK_TEXT; + info.m_itemId = index; + return InsertItem(info); +} + +// For list view mode (only), inserts a column. +long wxListCtrl::InsertColumn(long col, wxListItem& item) +{ + if (m_genericImpl) + return m_genericImpl->InsertColumn(col, item); + + if (m_dbImpl) + { + int width = item.GetWidth(); + if ( !(item.GetMask() & wxLIST_MASK_WIDTH) ) + width = 150; + + DataBrowserPropertyType type = kDataBrowserCustomType; //kDataBrowserTextType; + wxImageList* imageList = GetImageList(wxIMAGE_LIST_SMALL); + if (imageList && imageList->GetImageCount() > 0) + { + wxBitmap bmp = imageList->GetBitmap(0); + //if (bmp.Ok()) + // type = kDataBrowserIconAndTextType; + } + + SInt16 just = teFlushDefault; + if (item.GetMask() & wxLIST_MASK_FORMAT) + { + if (item.GetAlign() == wxLIST_FORMAT_LEFT) + just = teFlushLeft; + else if (item.GetAlign() == wxLIST_FORMAT_CENTER) + just = teCenter; + else if (item.GetAlign() == wxLIST_FORMAT_RIGHT) + just = teFlushRight; + } + m_dbImpl->InsertColumn(col, type, item.GetText(), just, width); + SetColumn(col, item); + + // set/remove options based on the wxListCtrl type. + DataBrowserTableViewColumnID id; + m_dbImpl->GetColumnIDFromIndex(col, &id); + DataBrowserPropertyFlags flags; + verify_noerr(m_dbImpl->GetPropertyFlags(id, &flags)); + if (GetWindowStyleFlag() & wxLC_EDIT_LABELS) + flags |= kDataBrowserPropertyIsEditable; + + if (GetWindowStyleFlag() & wxLC_VIRTUAL){ + flags &= ~kDataBrowserListViewSortableColumn; + } + verify_noerr(m_dbImpl->SetPropertyFlags(id, flags)); + } + + return col; +} + +long wxListCtrl::InsertColumn(long col, + const wxString& heading, + int format, + int width) +{ + if (m_genericImpl) + return m_genericImpl->InsertColumn(col, heading, format, width); + + wxListItem item; + item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT; + item.m_text = heading; + if ( width > -1 ) + { + item.m_mask |= wxLIST_MASK_WIDTH; + item.m_width = width; + } + item.m_format = format; + + return InsertColumn(col, item); +} + +// scroll the control by the given number of pixels (exception: in list view, +// dx is interpreted as number of columns) +bool wxListCtrl::ScrollList(int dx, int dy) +{ + if (m_genericImpl) + return m_genericImpl->ScrollList(dx, dy); + + if (m_dbImpl) + { + m_dbImpl->SetScrollPosition(dx, dy); + } + return true; +} + + +bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data) +{ + if (m_genericImpl) + return m_genericImpl->SortItems(fn, data); + + if (m_dbImpl) + { + m_compareFunc = fn; + m_compareFuncData = data; + SortDataBrowserContainer( m_dbImpl->GetControlRef(), kDataBrowserNoItem, true); + + // we need to do this after each call, else we get a crash from wxPython when + // SortItems is called the second time. + m_compareFunc = NULL; + m_compareFuncData = 0; + } + + return true; +} + +void wxListCtrl::OnRenameTimer() +{ + wxCHECK_RET( HasCurrent(), wxT("unexpected rename timer") ); + + EditLabel( m_current ); +} + +bool wxListCtrl::OnRenameAccept(long itemEdit, const wxString& value) +{ + wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetId() ); + le.SetEventObject( this ); + le.m_itemIndex = itemEdit; + + GetItem( le.m_item ); + le.m_item.m_text = value; + return !HandleWindowEvent( le ) || + le.IsAllowed(); +} + +void wxListCtrl::OnRenameCancelled(long itemEdit) +{ + // let owner know that the edit was cancelled + wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() ); + + le.SetEditCanceled(true); + + le.SetEventObject( this ); + le.m_itemIndex = itemEdit; + + GetItem( le.m_item ); + HandleWindowEvent( le ); +} + +// ---------------------------------------------------------------------------- +// virtual list controls +// ---------------------------------------------------------------------------- + +wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) const +{ + // this is a pure virtual function, in fact - which is not really pure + // because the controls which are not virtual don't need to implement it + wxFAIL_MSG( _T("wxListCtrl::OnGetItemText not supposed to be called") ); + + return wxEmptyString; +} + +int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const +{ + wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL), + -1, + wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden.")); + return -1; +} + +int wxListCtrl::OnGetItemColumnImage(long item, long column) const +{ + if (!column) + return OnGetItemImage(item); + + return -1; +} + +wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const +{ + wxASSERT_MSG( item >= 0 && item < GetItemCount(), + _T("invalid item index in OnGetItemAttr()") ); + + // no attributes by default + return NULL; +} + +void wxListCtrl::SetItemCount(long count) +{ + wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") ); + + if (m_genericImpl) + { + m_genericImpl->SetItemCount(count); + return; + } + + if (m_dbImpl) + { + // we need to temporarily disable the new item creation notification + // procedure to speed things up + // FIXME: Even this doesn't seem to help much... + + // FIXME: Find a more efficient way to do this. + m_dbImpl->MacClear(); + + DataBrowserCallbacks callbacks; + DataBrowserItemNotificationUPP itemUPP; + GetDataBrowserCallbacks(m_dbImpl->GetControlRef(), &callbacks); + itemUPP = callbacks.u.v1.itemNotificationCallback; + callbacks.u.v1.itemNotificationCallback = 0; + m_dbImpl->SetCallbacks(&callbacks); + ::AddDataBrowserItems(m_dbImpl->GetControlRef(), kDataBrowserNoItem, + count, NULL, kDataBrowserItemNoProperty); + callbacks.u.v1.itemNotificationCallback = itemUPP; + m_dbImpl->SetCallbacks(&callbacks); + } + m_count = count; +} + +void wxListCtrl::RefreshItem(long item) +{ + if (m_genericImpl) + { + m_genericImpl->RefreshItem(item); + return; + } + + if (m_dbImpl) + { + DataBrowserItemID id; + + if ( !IsVirtual() ) + { + wxMacDataItem* thisItem = m_dbImpl->GetItemFromLine(item); + id = (DataBrowserItemID) thisItem; + } + else + id = item+1; + + m_dbImpl->wxMacDataBrowserControl::UpdateItems + ( + kDataBrowserNoItem, + 1, &id, + kDataBrowserItemNoProperty, // preSortProperty + kDataBrowserNoItem // update all columns + ); + } +} + +void wxListCtrl::RefreshItems(long itemFrom, long itemTo) +{ + if (m_genericImpl) + { + m_genericImpl->RefreshItems(itemFrom, itemTo); + return; + } + + if (m_dbImpl) + { + const long count = itemTo - itemFrom + 1; + DataBrowserItemID *ids = new DataBrowserItemID[count]; + + if ( !IsVirtual() ) + { + for ( long i = 0; i < count; i++ ) + { + wxMacDataItem* thisItem = m_dbImpl->GetItemFromLine(itemFrom+i); + ids[i] = (DataBrowserItemID) thisItem; + } + } + else + { + for ( long i = 0; i < count; i++ ) + ids[i] = itemFrom+i+1; + } + + m_dbImpl->wxMacDataBrowserControl::UpdateItems + ( + kDataBrowserNoItem, + count, ids, + kDataBrowserItemNoProperty, // preSortProperty + kDataBrowserNoItem // update all columns + ); + + delete[] ids; + } +} + +void wxListCtrl::SetDropTarget( wxDropTarget *dropTarget ) +{ +#if wxUSE_DRAG_AND_DROP + if (m_genericImpl) + m_genericImpl->SetDropTarget( dropTarget ); + + if (m_dbImpl) + wxWindow::SetDropTarget( dropTarget ); +#endif +} + +wxDropTarget *wxListCtrl::GetDropTarget() const +{ +#if wxUSE_DRAG_AND_DROP + if (m_genericImpl) + return m_genericImpl->GetDropTarget(); + + if (m_dbImpl) + return wxWindow::GetDropTarget(); +#endif + return NULL; +} + +#if wxABI_VERSION >= 20801 +void wxListCtrl::SetFocus() +{ + if (m_genericImpl) + { + m_genericImpl->SetFocus(); + return; + } + + wxWindow::SetFocus(); +} +#endif + +// wxMac internal data structures + +wxMacListCtrlItem::~wxMacListCtrlItem() +{ + WX_CLEAR_HASH_MAP( wxListItemList, m_rowItems ); +} + +void wxMacListCtrlItem::Notification(wxMacDataItemBrowserControl *owner , + DataBrowserItemNotification message, + DataBrowserItemDataRef WXUNUSED(itemData) ) const +{ + + wxMacDataBrowserListCtrlControl *lb = wxDynamicCast(owner, wxMacDataBrowserListCtrlControl); + + // we want to depend on as little as possible to make sure tear-down of controls is safe + if ( message == kDataBrowserItemRemoved) + { + if ( lb != NULL && lb->GetClientDataType() == wxClientData_Object ) + { + delete (wxClientData*) (m_data); + } + + delete this; + return; + } + else if ( message == kDataBrowserItemAdded ) + { + // we don't issue events on adding, the item is not really stored in the list yet, so we + // avoid asserts by gettting out now + return ; + } + + wxListCtrl *list = wxDynamicCast( owner->GetPeer() , wxListCtrl ); + if ( list && lb ) + { + bool trigger = false; + + wxListEvent event( wxEVT_COMMAND_LIST_ITEM_SELECTED, list->GetId() ); + bool isSingle = (list->GetWindowStyle() & wxLC_SINGLE_SEL) != 0; + + event.SetEventObject( list ); + event.m_itemIndex = owner->GetLineFromItem( this ) ; + event.m_item.m_itemId = event.m_itemIndex; + list->GetItem(event.m_item); + + switch (message) + { + case kDataBrowserItemDeselected: + event.SetEventType(wxEVT_COMMAND_LIST_ITEM_DESELECTED); + if ( !isSingle ) + trigger = !lb->IsSelectionSuppressed(); + break; + + case kDataBrowserItemSelected: + trigger = !lb->IsSelectionSuppressed(); + break; + + case kDataBrowserItemDoubleClicked: + event.SetEventType( wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); + trigger = true; + break; + + case kDataBrowserEditStarted : + // TODO : how to veto ? + event.SetEventType( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT ) ; + trigger = true ; + break ; + + case kDataBrowserEditStopped : + // TODO probably trigger only upon the value store callback, because + // here IIRC we cannot veto + event.SetEventType( wxEVT_COMMAND_LIST_END_LABEL_EDIT ) ; + trigger = true ; + break ; + + default: + break; + } + + if ( trigger ) + { + // direct notification is not always having the listbox GetSelection() having in synch with event + wxPostEvent( list->GetEventHandler(), event ); + } + } + +} + +IMPLEMENT_DYNAMIC_CLASS(wxMacDataBrowserListCtrlControl, wxMacDataItemBrowserControl ) + +wxMacDataBrowserListCtrlControl::wxMacDataBrowserListCtrlControl( wxWindow *peer, const wxPoint& pos, const wxSize& size, long style) + : wxMacDataItemBrowserControl( peer, pos, size, style ) +{ + OSStatus err = noErr; + m_clientDataItemsType = wxClientData_None; + m_isVirtual = false; + m_flags = 0; + + if ( style & wxLC_VIRTUAL ) + m_isVirtual = true; + + DataBrowserSelectionFlags options = kDataBrowserDragSelect; + if ( style & wxLC_SINGLE_SEL ) + { + options |= kDataBrowserSelectOnlyOne; + } + else + { + options |= kDataBrowserCmdTogglesSelection; + } + + err = SetSelectionFlags( options ); + verify_noerr( err ); + + DataBrowserCustomCallbacks callbacks; + InitializeDataBrowserCustomCallbacks( &callbacks, kDataBrowserLatestCustomCallbacks ); + + if ( gDataBrowserDrawItemUPP == NULL ) + gDataBrowserDrawItemUPP = NewDataBrowserDrawItemUPP(DataBrowserDrawItemProc); + + if ( gDataBrowserHitTestUPP == NULL ) + gDataBrowserHitTestUPP = NewDataBrowserHitTestUPP(DataBrowserHitTestProc); + + callbacks.u.v1.drawItemCallback = gDataBrowserDrawItemUPP; + callbacks.u.v1.hitTestCallback = gDataBrowserHitTestUPP; + + SetDataBrowserCustomCallbacks( GetControlRef(), &callbacks ); + + if ( style & wxLC_LIST ) + { + InsertColumn(0, kDataBrowserIconAndTextType, wxEmptyString, -1, -1); + verify_noerr( AutoSizeColumns() ); + } + + if ( style & wxLC_LIST || style & wxLC_NO_HEADER ) + verify_noerr( SetHeaderButtonHeight( 0 ) ); + + if ( m_isVirtual ) + SetSortProperty( kMinColumnId - 1 ); + else + SetSortProperty( kMinColumnId ); + + m_sortOrder = SortOrder_None; + + if ( style & wxLC_SORT_DESCENDING ) + { + SetSortOrder( kDataBrowserOrderDecreasing ); + } + else if ( style & wxLC_SORT_ASCENDING ) + { + SetSortOrder( kDataBrowserOrderIncreasing ); + } + + if ( style & wxLC_VRULES ) + { + verify_noerr( DataBrowserChangeAttributes(m_controlRef, kDataBrowserAttributeListViewDrawColumnDividers, kDataBrowserAttributeNone) ); + } + + verify_noerr( SetHiliteStyle(kDataBrowserTableViewFillHilite ) ); + verify_noerr( SetHasScrollBars( (style & wxHSCROLL) != 0 , true ) ); +} + +pascal Boolean wxMacDataBrowserListCtrlControl::DataBrowserEditTextProc( + ControlRef browser, + DataBrowserItemID itemID, + DataBrowserPropertyID property, + CFStringRef theString, + Rect *maxEditTextRect, + Boolean *shrinkToFit) +{ + Boolean result = false; + wxMacDataBrowserListCtrlControl* ctl = wxDynamicCast(wxMacControl::GetReferenceFromNativeControl( browser ), wxMacDataBrowserListCtrlControl); + if ( ctl != 0 ) + { + result = ctl->ConfirmEditText(itemID, property, theString, maxEditTextRect, shrinkToFit); + theString = CFSTR("Hello!"); + } + return result; +} + +bool wxMacDataBrowserListCtrlControl::ConfirmEditText( + DataBrowserItemID WXUNUSED(itemID), + DataBrowserPropertyID WXUNUSED(property), + CFStringRef WXUNUSED(theString), + Rect *WXUNUSED(maxEditTextRect), + Boolean *WXUNUSED(shrinkToFit)) +{ + return false; +} + +pascal void wxMacDataBrowserListCtrlControl::DataBrowserDrawItemProc( + ControlRef browser, + DataBrowserItemID itemID, + DataBrowserPropertyID property, + DataBrowserItemState itemState, + const Rect *itemRect, + SInt16 gdDepth, + Boolean colorDevice) +{ + wxMacDataBrowserListCtrlControl* ctl = wxDynamicCast(wxMacControl::GetReferenceFromNativeControl( browser ), wxMacDataBrowserListCtrlControl); + if ( ctl != 0 ) + { + ctl->DrawItem(itemID, property, itemState, itemRect, gdDepth, colorDevice); + } +} + +// routines needed for DrawItem +enum +{ + kIconWidth = 16, + kIconHeight = 16, + kTextBoxHeight = 14, + kIconTextSpacingV = 2, + kItemPadding = 4, + kContentHeight = kIconHeight + kTextBoxHeight + kIconTextSpacingV +}; + +static void calculateCGDrawingBounds(CGRect inItemRect, CGRect *outIconRect, CGRect *outTextRect, bool hasIcon = false) +{ + float textBottom; + float iconH, iconW = 0; + float padding = kItemPadding; + if (hasIcon) + { + iconH = kIconHeight; + iconW = kIconWidth; + padding = padding*2; + } + + textBottom = inItemRect.origin.y; + + *outIconRect = CGRectMake(inItemRect.origin.x + kItemPadding, + textBottom + kIconTextSpacingV, kIconWidth, + kIconHeight); + + *outTextRect = CGRectMake(inItemRect.origin.x + padding + iconW, + textBottom + kIconTextSpacingV, inItemRect.size.width - padding - iconW, + inItemRect.size.height - kIconTextSpacingV); +} + +void wxMacDataBrowserListCtrlControl::DrawItem( + DataBrowserItemID itemID, + DataBrowserPropertyID property, + DataBrowserItemState itemState, + const Rect *WXUNUSED(itemRect), + SInt16 gdDepth, + Boolean colorDevice) +{ + wxString text; + wxFont font = wxNullFont; + int imgIndex = -1; + short listColumn = property - kMinColumnId; + + wxListCtrl* list = wxDynamicCast( GetPeer() , wxListCtrl ); + wxMacListCtrlItem* lcItem; + wxColour color = *wxBLACK; + wxColour bgColor = wxNullColour; + + if (listColumn >= 0) + { + if (!m_isVirtual) + { + lcItem = (wxMacListCtrlItem*) itemID; + if (lcItem->HasColumnInfo(listColumn)){ + wxListItem* item = lcItem->GetColumnInfo(listColumn); + + // we always use the 0 column to get font and text/background colors. + if (lcItem->HasColumnInfo(0)) + { + wxListItem* firstItem = lcItem->GetColumnInfo(0); + color = firstItem->GetTextColour(); + bgColor = firstItem->GetBackgroundColour(); + font = firstItem->GetFont(); + } + + if (item->GetMask() & wxLIST_MASK_TEXT) + text = item->GetText(); + if (item->GetMask() & wxLIST_MASK_IMAGE) + imgIndex = item->GetImage(); + } + + } + else + { + long itemNum = (long)itemID-1; + if (itemNum >= 0 && itemNum < list->GetItemCount()) + { + text = list->OnGetItemText( itemNum, listColumn ); + imgIndex = list->OnGetItemColumnImage( itemNum, listColumn ); + wxListItemAttr* attrs = list->OnGetItemAttr( itemNum ); + if (attrs) + { + if (attrs->HasBackgroundColour()) + bgColor = attrs->GetBackgroundColour(); + if (attrs->HasTextColour()) + color = attrs->GetTextColour(); + if (attrs->HasFont()) + font = attrs->GetFont(); + } + } + } + } + + wxColour listBgColor = list->GetBackgroundColour(); + if (bgColor == wxNullColour) + bgColor = listBgColor; + + if (!font.Ok()) + font = list->GetFont(); + + wxCFStringRef cfString( text, wxLocale::GetSystemEncoding() ); + + Rect enclosingRect; + CGRect enclosingCGRect, iconCGRect, textCGRect; + Boolean active; + ThemeDrawingState savedState = NULL; + CGContextRef context = (CGContextRef)list->MacGetDrawingContext(); + RGBColor labelColor; + labelColor.red = 0; + labelColor.green = 0; + labelColor.blue = 0; + + RGBColor backgroundColor; + backgroundColor.red = 255; + backgroundColor.green = 255; + backgroundColor.blue = 255; + + GetDataBrowserItemPartBounds(GetControlRef(), itemID, property, kDataBrowserPropertyEnclosingPart, + &enclosingRect); + + enclosingCGRect = CGRectMake(enclosingRect.left, + enclosingRect.top, + enclosingRect.right - enclosingRect.left, + enclosingRect.bottom - enclosingRect.top); + + bool hasFocus = (wxWindow::FindFocus() == list); + active = IsControlActive(GetControlRef()); + + // don't paint the background over the vertical rule line + if ( list->GetWindowStyleFlag() & wxLC_VRULES ) + { + enclosingCGRect.origin.x += 1; + enclosingCGRect.size.width -= 1; + } + if (itemState == kDataBrowserItemIsSelected) + { + + GetThemeDrawingState(&savedState); + + if (active && hasFocus) + { + GetThemeBrushAsColor(kThemeBrushAlternatePrimaryHighlightColor, 32, true, &backgroundColor); + GetThemeTextColor(kThemeTextColorWhite, gdDepth, colorDevice, &labelColor); + } + else + { + GetThemeBrushAsColor(kThemeBrushSecondaryHighlightColor, 32, true, &backgroundColor); + GetThemeTextColor(kThemeTextColorBlack, gdDepth, colorDevice, &labelColor); + } + CGContextSaveGState(context); + + CGContextSetRGBFillColor(context, (CGFloat)backgroundColor.red / (CGFloat)USHRT_MAX, + (CGFloat)backgroundColor.green / (CGFloat)USHRT_MAX, + (CGFloat)backgroundColor.blue / (CGFloat)USHRT_MAX, (CGFloat) 1.0); + CGContextFillRect(context, enclosingCGRect); + + CGContextRestoreGState(context); + } + else + { + + if (color.Ok()) + color.GetRGBColor(&labelColor); + else if (list->GetTextColour().Ok()) + list->GetTextColour().GetRGBColor(&labelColor); + + if (bgColor.Ok()) + { + bgColor.GetRGBColor(&backgroundColor); + CGContextSaveGState(context); + + CGContextSetRGBFillColor(context, (CGFloat)backgroundColor.red / (CGFloat)USHRT_MAX, + (CGFloat)backgroundColor.green / (CGFloat)USHRT_MAX, + (CGFloat)backgroundColor.blue / (CGFloat)USHRT_MAX, (CGFloat) 1.0); + CGContextFillRect(context, enclosingCGRect); + + CGContextRestoreGState(context); + } + } + + calculateCGDrawingBounds(enclosingCGRect, &iconCGRect, &textCGRect, (imgIndex != -1) ); + + if (imgIndex != -1) + { + wxImageList* imageList = list->GetImageList(wxIMAGE_LIST_SMALL); + if (imageList && imageList->GetImageCount() > 0){ + wxBitmap bmp = imageList->GetBitmap(imgIndex); + IconRef icon = bmp.GetIconRef(); + + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0,iconCGRect.origin.y + CGRectGetMaxY(iconCGRect)); + CGContextScaleCTM(context,1.0f,-1.0f); + PlotIconRefInContext(context, &iconCGRect, kAlignNone, + active ? kTransformNone : kTransformDisabled, NULL, + kPlotIconRefNormalFlags, icon); + + CGContextRestoreGState(context); + } + } + + HIThemeTextHorizontalFlush hFlush = kHIThemeTextHorizontalFlushLeft; + HIThemeTextInfo info; + bool setup = false; +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + info.version = kHIThemeTextInfoVersionOne; + info.fontID = kThemeViewsFont; + if (font.Ok()) + { + info.fontID = kThemeSpecifiedFont; + info.font = (CTFontRef) font.MacGetCTFont(); + setup = true; + } + } +#endif +#if wxMAC_USE_ATSU_TEXT + if ( !setup ) + { + info.version = kHIThemeTextInfoVersionZero; + info.fontID = kThemeViewsFont; + + if (font.Ok()) + { + info.fontID = font.MacGetThemeFontID(); + + ::TextSize( (short)(font.MacGetFontSize()) ) ; + ::TextFace( font.MacGetFontStyle() ) ; + } + } +#endif + + wxListItem item; + list->GetColumn(listColumn, item); + if (item.GetMask() & wxLIST_MASK_FORMAT) + { + if (item.GetAlign() == wxLIST_FORMAT_LEFT) + hFlush = kHIThemeTextHorizontalFlushLeft; + else if (item.GetAlign() == wxLIST_FORMAT_CENTER) + hFlush = kHIThemeTextHorizontalFlushCenter; + else if (item.GetAlign() == wxLIST_FORMAT_RIGHT) + { + hFlush = kHIThemeTextHorizontalFlushRight; + textCGRect.origin.x -= kItemPadding; // give a little extra paddding + } + } + + info.state = active ? kThemeStateActive : kThemeStateInactive; + info.horizontalFlushness = hFlush; + info.verticalFlushness = kHIThemeTextVerticalFlushCenter; + info.options = kHIThemeTextBoxOptionNone; + info.truncationPosition = kHIThemeTextTruncationEnd; + info.truncationMaxLines = 1; + + CGContextSaveGState(context); + CGContextSetRGBFillColor (context, (CGFloat)labelColor.red / (CGFloat)USHRT_MAX, + (CGFloat)labelColor.green / (CGFloat)USHRT_MAX, + (CGFloat)labelColor.blue / (CGFloat)USHRT_MAX, (CGFloat) 1.0); + + HIThemeDrawTextBox(cfString, &textCGRect, &info, context, kHIThemeOrientationNormal); + + CGContextRestoreGState(context); + +#ifndef __LP64__ + if (savedState != NULL) + SetThemeDrawingState(savedState, true); +#endif +} + +OSStatus wxMacDataBrowserListCtrlControl::GetSetItemData(DataBrowserItemID itemID, + DataBrowserPropertyID property, + DataBrowserItemDataRef itemData, + Boolean changeValue ) +{ + wxString text; + int imgIndex = -1; + short listColumn = property - kMinColumnId; + + OSStatus err = errDataBrowserPropertyNotSupported; + wxListCtrl* list = wxDynamicCast( GetPeer() , wxListCtrl ); + wxMacListCtrlItem* lcItem = NULL; + + if (listColumn >= 0) + { + if (!m_isVirtual) + { + lcItem = (wxMacListCtrlItem*) itemID; + if (lcItem && lcItem->HasColumnInfo(listColumn)){ + wxListItem* item = lcItem->GetColumnInfo(listColumn); + if (item->GetMask() & wxLIST_MASK_TEXT) + text = item->GetText(); + if (item->GetMask() & wxLIST_MASK_IMAGE) + imgIndex = item->GetImage(); + } + } + else + { + long itemNum = (long)itemID-1; + if (itemNum >= 0 && itemNum < list->GetItemCount()) + { + text = list->OnGetItemText( itemNum, listColumn ); + imgIndex = list->OnGetItemColumnImage( itemNum, listColumn ); + } + } + } + + if ( !changeValue ) + { + switch (property) + { + case kDataBrowserItemIsEditableProperty : + if ( list && list->HasFlag( wxLC_EDIT_LABELS ) ) + { + verify_noerr(SetDataBrowserItemDataBooleanValue( itemData, true )); + err = noErr ; + } + break ; + default : + if ( property >= kMinColumnId ) + { + if (!text.IsEmpty()){ + wxCFStringRef cfStr( text, wxLocale::GetSystemEncoding() ); + err = ::SetDataBrowserItemDataText( itemData, cfStr ); + err = noErr; + } + + + + if ( imgIndex != -1 ) + { + wxImageList* imageList = list->GetImageList(wxIMAGE_LIST_SMALL); + if (imageList && imageList->GetImageCount() > 0){ + wxBitmap bmp = imageList->GetBitmap(imgIndex); + IconRef icon = bmp.GetIconRef(); + ::SetDataBrowserItemDataIcon(itemData, icon); + } + } + + } + break ; + } + + } + else + { + switch (property) + { + default: + if ( property >= kMinColumnId ) + { + short listColumn = property - kMinColumnId; + + // TODO probably send the 'end edit' from here, as we + // can then deal with the veto + CFStringRef sr ; + verify_noerr( GetDataBrowserItemDataText( itemData , &sr ) ) ; + wxCFStringRef cfStr(sr) ;; + if (m_isVirtual) + list->SetItem( (long)itemData-1 , listColumn, cfStr.AsString() ) ; + else + { + if (lcItem) + lcItem->SetColumnTextValue( listColumn, cfStr.AsString() ); + } + err = noErr ; + } + break; + } + } + return err; +} + +void wxMacDataBrowserListCtrlControl::ItemNotification(DataBrowserItemID itemID, + DataBrowserItemNotification message, + DataBrowserItemDataRef itemData ) +{ + wxMacListCtrlItem *item = NULL; + if ( !m_isVirtual ) + { + item = (wxMacListCtrlItem *) itemID; + } + + // we want to depend on as little as possible to make sure tear-down of controls is safe + if ( message == kDataBrowserItemRemoved ) + { + if ( item ) + item->Notification(this, message, itemData); + return; + } + else if ( message == kDataBrowserItemAdded ) + { + // we don't issue events on adding, the item is not really stored in the list yet, so we + // avoid asserts by getting out now + if ( item ) + item->Notification(this, message, itemData); + return ; + } + + wxListCtrl *list = wxDynamicCast( GetPeer() , wxListCtrl ); + if ( list ) + { + bool trigger = false; + + wxListEvent event( wxEVT_COMMAND_LIST_ITEM_SELECTED, list->GetId() ); + + event.SetEventObject( list ); + if ( !list->IsVirtual() ) + { + DataBrowserTableViewRowIndex result = 0; + verify_noerr( GetItemRow( itemID, &result ) ) ; + event.m_itemIndex = result; + } + else + { + event.m_itemIndex = (long)itemID-1; + } + event.m_item.m_itemId = event.m_itemIndex; + list->GetItem(event.m_item); + + switch (message) + { + case kDataBrowserItemDeselected: + event.SetEventType(wxEVT_COMMAND_LIST_ITEM_DESELECTED); + // as the generic implementation is also triggering this + // event for single selection, we do the same (different than listbox) + trigger = !IsSelectionSuppressed(); + break; + + case kDataBrowserItemSelected: + trigger = !IsSelectionSuppressed(); + + break; + + case kDataBrowserItemDoubleClicked: + event.SetEventType( wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); + trigger = true; + break; + + case kDataBrowserEditStarted : + // TODO : how to veto ? + event.SetEventType( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT ) ; + trigger = true ; + break ; + + case kDataBrowserEditStopped : + // TODO probably trigger only upon the value store callback, because + // here IIRC we cannot veto + event.SetEventType( wxEVT_COMMAND_LIST_END_LABEL_EDIT ) ; + trigger = true ; + break ; + + default: + break; + } + + if ( trigger ) + { + // direct notification is not always having the listbox GetSelection() having in synch with event + wxPostEvent( list->GetEventHandler(), event ); + } + } +} + +Boolean wxMacDataBrowserListCtrlControl::CompareItems(DataBrowserItemID itemOneID, + DataBrowserItemID itemTwoID, + DataBrowserPropertyID sortProperty) +{ + + bool retval = false; + wxString itemText; + wxString otherItemText; + long itemOrder; + long otherItemOrder; + + int colId = sortProperty - kMinColumnId; + + wxListCtrl* list = wxDynamicCast( GetPeer() , wxListCtrl ); + + DataBrowserSortOrder sort; + verify_noerr(GetSortOrder(&sort)); + + if (colId >= 0) + { + if (!m_isVirtual) + { + wxMacListCtrlItem* item = (wxMacListCtrlItem*)itemOneID; + wxMacListCtrlItem* otherItem = (wxMacListCtrlItem*)itemTwoID; + + itemOrder = item->GetOrder(); + otherItemOrder = item->GetOrder(); + + wxListCtrlCompare func = list->GetCompareFunc(); + if (func != NULL) + { + long item1 = -1; + long item2 = -1; + if (item && item->HasColumnInfo(0)) + item1 = item->GetColumnInfo(0)->GetData(); + if (otherItem && otherItem->HasColumnInfo(0)) + item2 = otherItem->GetColumnInfo(0)->GetData(); + + if (item1 > -1 && item2 > -1) + { + int result = func(item1, item2, list->GetCompareFuncData()); + if (sort == kDataBrowserOrderIncreasing) + return result >= 0; + else + return result < 0; + } + } + + // we can't use the native control's sorting abilities, so just + // sort by item id. + return itemOrder < otherItemOrder; + } + else + { + + long itemNum = (long)itemOneID; + long otherItemNum = (long)itemTwoID; + + // virtual listctrls don't support sorting + return itemNum < otherItemNum; + } + } + else{ + // fallback for undefined cases + retval = itemOneID < itemTwoID; + } + + return retval; +} + +wxMacDataBrowserListCtrlControl::~wxMacDataBrowserListCtrlControl() +{ +} + +void wxMacDataBrowserListCtrlControl::MacSetColumnInfo( unsigned int row, unsigned int column, wxListItem* item ) +{ + wxMacDataItem* dataItem = GetItemFromLine(row); + wxASSERT_MSG( dataItem, _T("could not obtain wxMacDataItem for row in MacSetColumnInfo. Is row a valid wxListCtrl row?") ); + if (item) + { + wxMacListCtrlItem* listItem = wx_static_cast(wxMacListCtrlItem*,dataItem); + bool hasInfo = listItem->HasColumnInfo( column ); + listItem->SetColumnInfo( column, item ); + listItem->SetOrder(row); + UpdateState(dataItem, item); + + wxListCtrl* list = wxDynamicCast( GetPeer() , wxListCtrl ); + + // NB: When this call was made before a control was completely shown, it would + // update the item prematurely (i.e. no text would be listed) and, on show, + // only the sorted column would be refreshed, meaning only first column text labels + // would be shown. Making sure not to update items until the control is visible + // seems to fix this issue. + if (hasInfo && list->IsShown()) + UpdateItem( wxMacDataBrowserRootContainer, listItem , kMinColumnId + column ); + } +} + +// apply changes that need to happen immediately, rather than when the +// databrowser control fires a callback. +void wxMacDataBrowserListCtrlControl::UpdateState(wxMacDataItem* dataItem, wxListItem* listItem) +{ + bool isSelected = IsItemSelected( dataItem ); + bool isSelectedState = (listItem->GetState() == wxLIST_STATE_SELECTED); + + // toggle the selection state if wxListInfo state and actual state don't match. + if ( listItem->GetMask() & wxLIST_MASK_STATE && isSelected != isSelectedState ) + { + DataBrowserSetOption options = kDataBrowserItemsAdd; + if (!isSelectedState) + options = kDataBrowserItemsRemove; + SetSelectedItem(dataItem, options); + } + // TODO: Set column width if item width > than current column width +} + +void wxMacDataBrowserListCtrlControl::MacGetColumnInfo( unsigned int row, unsigned int column, wxListItem& item ) +{ + wxMacDataItem* dataItem = GetItemFromLine(row); + wxASSERT_MSG( dataItem, _T("could not obtain wxMacDataItem in MacGetColumnInfo. Is row a valid wxListCtrl row?") ); + // CS should this guard against dataItem = 0 ? , as item is not a pointer if (item) is not appropriate + //if (item) + { + wxMacListCtrlItem* listItem =wx_static_cast(wxMacListCtrlItem*,dataItem); + + if (!listItem->HasColumnInfo( column )) + return; + + wxListItem* oldItem = listItem->GetColumnInfo( column ); + + if (oldItem) + { + long mask = item.GetMask(); + if ( !mask ) + // by default, get everything for backwards compatibility + mask = -1; + + if ( mask & wxLIST_MASK_TEXT ) + item.SetText(oldItem->GetText()); + if ( mask & wxLIST_MASK_IMAGE ) + item.SetImage(oldItem->GetImage()); + if ( mask & wxLIST_MASK_DATA ) + item.SetData(oldItem->GetData()); + if ( mask & wxLIST_MASK_STATE ) + item.SetState(oldItem->GetState()); + if ( mask & wxLIST_MASK_WIDTH ) + item.SetWidth(oldItem->GetWidth()); + if ( mask & wxLIST_MASK_FORMAT ) + item.SetAlign(oldItem->GetAlign()); + + item.SetTextColour(oldItem->GetTextColour()); + item.SetBackgroundColour(oldItem->GetBackgroundColour()); + item.SetFont(oldItem->GetFont()); + } + } +} + +void wxMacDataBrowserListCtrlControl::MacInsertItem( unsigned int n, wxListItem* item ) +{ + wxMacDataItemBrowserControl::MacInsert(n, item->GetText()); + MacSetColumnInfo(n, 0, item); +} + +wxMacDataItem* wxMacDataBrowserListCtrlControl::CreateItem() +{ + return new wxMacListCtrlItem(); +} + +wxMacListCtrlItem::wxMacListCtrlItem() +{ + m_rowItems = wxListItemList(); +} + +int wxMacListCtrlItem::GetColumnImageValue( unsigned int column ) +{ + if ( HasColumnInfo(column) ) + return GetColumnInfo(column)->GetImage(); + + return -1; +} + +void wxMacListCtrlItem::SetColumnImageValue( unsigned int column, int imageIndex ) +{ + if ( HasColumnInfo(column) ) + GetColumnInfo(column)->SetImage(imageIndex); +} + +wxString wxMacListCtrlItem::GetColumnTextValue( unsigned int column ) +{ + if ( column == 0 ) + return GetLabel(); + + if ( HasColumnInfo(column) ) + return GetColumnInfo(column)->GetText(); + + return wxEmptyString; +} + +void wxMacListCtrlItem::SetColumnTextValue( unsigned int column, const wxString& text ) +{ + if ( HasColumnInfo(column) ) + GetColumnInfo(column)->SetText(text); + + // for compatibility with superclass APIs + if ( column == 0 ) + SetLabel(text); +} + +wxListItem* wxMacListCtrlItem::GetColumnInfo( unsigned int column ) +{ + wxASSERT_MSG( HasColumnInfo(column), _T("invalid column index in wxMacListCtrlItem") ); + return m_rowItems[column]; +} + +bool wxMacListCtrlItem::HasColumnInfo( unsigned int column ) +{ + return !(m_rowItems.find( column ) == m_rowItems.end()); +} + +void wxMacListCtrlItem::SetColumnInfo( unsigned int column, wxListItem* item ) +{ + + if ( !HasColumnInfo(column) ) + { + wxListItem* listItem = new wxListItem(*item); + m_rowItems[column] = listItem; + } + else + { + wxListItem* listItem = GetColumnInfo( column ); + long mask = item->GetMask(); + if (mask & wxLIST_MASK_TEXT) + listItem->SetText(item->GetText()); + if (mask & wxLIST_MASK_DATA) + listItem->SetData(item->GetData()); + if (mask & wxLIST_MASK_IMAGE) + listItem->SetImage(item->GetImage()); + if (mask & wxLIST_MASK_STATE) + listItem->SetState(item->GetState()); + if (mask & wxLIST_MASK_FORMAT) + listItem->SetAlign(item->GetAlign()); + if (mask & wxLIST_MASK_WIDTH) + listItem->SetWidth(item->GetWidth()); + + if ( item->HasAttributes() ) + { + if ( listItem->HasAttributes() ) + listItem->GetAttributes()->AssignFrom(*item->GetAttributes()); + else + { + listItem->SetTextColour(item->GetTextColour()); + listItem->SetBackgroundColour(item->GetBackgroundColour()); + listItem->SetFont(item->GetFont()); + } + } + } +} + +int wxListCtrl::CalcColumnAutoWidth(int col) const +{ + int width = 0; + + for ( int i = 0; i < GetItemCount(); i++ ) + { + wxListItem info; + info.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE); + info.SetId(i); + info.SetColumn(col); + GetItem(info); + + const wxFont font = info.GetFont(); + + int w = 0; + if ( font.IsOk() ) + GetTextExtent(info.GetText(), &w, NULL, NULL, NULL, &font); + else + GetTextExtent(info.GetText(), &w, NULL); + + w += 2 * kItemPadding; + + if ( info.GetImage() != -1 ) + w += kIconWidth; + + width = wxMax(width, w); + } + + return width; +} + +#endif // wxUSE_LISTCTRL + diff --git a/src/osx/carbon/main.cpp b/src/osx/carbon/main.cpp new file mode 100644 index 0000000000..ad5bd33263 --- /dev/null +++ b/src/osx/carbon/main.cpp @@ -0,0 +1,12 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: main.cpp +// Purpose: Entry point +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// We don't put main() in the library any more. GD. diff --git a/src/osx/carbon/mdi.cpp b/src/osx/carbon/mdi.cpp new file mode 100644 index 0000000000..ca02c69c0d --- /dev/null +++ b/src/osx/carbon/mdi.cpp @@ -0,0 +1,514 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/mdi.cpp +// Purpose: MDI classes +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_MDI + +#include "wx/mdi.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/menu.h" + #include "wx/settings.h" + #include "wx/statusbr.h" +#endif + +#include "wx/mac/private.h" +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame, wxFrame) +IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame, wxFrame) +IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow) + +BEGIN_EVENT_TABLE(wxMDIParentFrame, wxFrame) + EVT_ACTIVATE(wxMDIParentFrame::OnActivate) + EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged) +END_EVENT_TABLE() + +BEGIN_EVENT_TABLE(wxMDIClientWindow, wxWindow) + EVT_SCROLL(wxMDIClientWindow::OnScroll) +END_EVENT_TABLE() + +#define TRACE_MDI "mdi" + +static const int IDM_WINDOWTILEHOR = 4001; +static const int IDM_WINDOWCASCADE = 4002; +static const int IDM_WINDOWICONS = 4003; +static const int IDM_WINDOWNEXT = 4004; +static const int IDM_WINDOWTILEVERT = 4005; + +// others + +void UMAHighlightAndActivateWindow( WindowRef inWindowRef , bool inActivate ) +{ +#if 1 // TODO REMOVE + if ( inWindowRef ) + { +// bool isHighlighted = IsWindowHighlited( inWindowRef ) ; +// if ( inActivate != isHighlighted ) +#ifndef __LP64__ + GrafPtr port ; + GetPort( &port ) ; + SetPortWindowPort( inWindowRef ) ; +#endif + HiliteWindow( inWindowRef , inActivate ) ; + ControlRef control = NULL ; + ::GetRootControl( inWindowRef , &control ) ; + if ( control ) + { + if ( inActivate ) + ::ActivateControl( control ) ; + else + ::DeactivateControl( control ) ; + } +#ifndef __LP64__ + SetPort( port ) ; +#endif + } +#endif +} + +// ---------------------------------------------------------------------------- +// Parent frame +// ---------------------------------------------------------------------------- + +void wxMDIParentFrame::Init() +{ + m_clientWindow = NULL; + m_currentChild = NULL; + m_windowMenu = (wxMenu*) NULL; + m_parentFrameActive = true; + m_shouldBeShown = false; +} + +bool wxMDIParentFrame::Create(wxWindow *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) +{ + // this style can be used to prevent a window from having the standard MDI + // "Window" menu + if ( style & wxFRAME_NO_WINDOW_MENU ) + { + m_windowMenu = (wxMenu *)NULL; + style -= wxFRAME_NO_WINDOW_MENU ; + } + else // normal case: we have the window menu, so construct it + { + m_windowMenu = new wxMenu; + + m_windowMenu->Append(IDM_WINDOWCASCADE, wxT("&Cascade")); + m_windowMenu->Append(IDM_WINDOWTILEHOR, wxT("Tile &Horizontally")); + m_windowMenu->Append(IDM_WINDOWTILEVERT, wxT("Tile &Vertically")); + m_windowMenu->AppendSeparator(); + m_windowMenu->Append(IDM_WINDOWICONS, wxT("&Arrange Icons")); + m_windowMenu->Append(IDM_WINDOWNEXT, wxT("&Next")); + } + + if ( !wxFrame::Create( parent , id , title , pos , size , style , name ) ) + return false; + + m_parentFrameActive = true; + + m_clientWindow = OnCreateClient(); + + return m_clientWindow != NULL; +} + +wxMDIParentFrame::~wxMDIParentFrame() +{ + DestroyChildren(); + + // already deleted by DestroyChildren() + m_clientWindow = NULL ; + + delete m_windowMenu; +} + +void wxMDIParentFrame::SetMenuBar(wxMenuBar *menu_bar) +{ + wxFrame::SetMenuBar( menu_bar ) ; +} + +void wxMDIParentFrame::GetRectForTopLevelChildren(int *x, int *y, int *w, int *h) +{ + if (x) + *x = 0; + if (y) + *y = 0; + + wxDisplaySize(w, h); +} + +void wxMDIParentFrame::AddChild(wxWindowBase *child) +{ + // moved this to front, so that we don't run into unset m_parent problems later + wxFrame::AddChild(child); + + if ( !m_currentChild ) + { + m_currentChild = wxDynamicCast(child, wxMDIChildFrame); + + if ( m_currentChild && IsShown() && !ShouldBeVisible() ) + { + // we shouldn't remain visible any more + wxFrame::Show(false); + m_shouldBeShown = true; + } + } +} + +void wxMDIParentFrame::RemoveChild(wxWindowBase *child) +{ + if ( child == m_currentChild ) + { + // the current child isn't active any more, try to find another one + m_currentChild = NULL; + + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxMDIChildFrame * + childCur = wxDynamicCast(node->GetData(), wxMDIChildFrame); + if ( childCur != child ) + { + m_currentChild = childCur; + break; + } + } + } + + wxFrame::RemoveChild(child); + + // if there are no more children left we need to show the frame if we + // hadn't shown it before because there were active children and it was + // useless (note that we have to do it after fully removing the child, i.e. + // after calling the base class RemoveChild() as otherwise we risk to touch + // pointer to the child being deleted) + if ( !m_currentChild && m_shouldBeShown && !IsShown() ) + { + // we have to show it, but at least move it out of sight and make it of + // smallest possible size (unfortunately (0, 0) doesn't work so that it + // doesn't appear in expose + SetSize(-10000, -10000, 1, 1); + Show(); + } +} + +void wxMDIParentFrame::MacActivate(long timestamp, bool activating) +{ + wxLogTrace(TRACE_MDI, wxT("MDI PARENT=%p MacActivate(0x%08lx,%s)"), this, timestamp, activating ? wxT("ACTIV") : wxT("deact")); + + if (activating) + { + if (s_macDeactivateWindow && s_macDeactivateWindow->GetParent() == this) + { + wxLogTrace(TRACE_MDI, wxT("child had been scheduled for deactivation, rehighlighting")); + + UMAHighlightAndActivateWindow((WindowRef)s_macDeactivateWindow->MacGetWindowRef(), true); + + wxLogTrace(TRACE_MDI, wxT("finished highliting child")); + + s_macDeactivateWindow = NULL; + } + else if (s_macDeactivateWindow == this) + { + wxLogTrace(TRACE_MDI, wxT("Avoided deactivation/activation of this=%p"), this); + + s_macDeactivateWindow = NULL; + } + else // window to deactivate is NULL or is not us or one of our kids + { + // activate kid instead + if (m_currentChild) + m_currentChild->MacActivate(timestamp, activating); + else + wxFrame::MacActivate(timestamp, activating); + } + } + else + { + // We were scheduled for deactivation, and now we do it. + if (s_macDeactivateWindow == this) + { + s_macDeactivateWindow = NULL; + if (m_currentChild) + m_currentChild->MacActivate(timestamp, activating); + wxFrame::MacActivate(timestamp, activating); + } + else // schedule ourselves for deactivation + { + if (s_macDeactivateWindow) + wxLogTrace(TRACE_MDI, wxT("window=%p SHOULD have been deactivated, oh well!"), s_macDeactivateWindow); + wxLogTrace(TRACE_MDI, wxT("Scheduling delayed MDI Parent deactivation")); + + s_macDeactivateWindow = this; + } + } +} + +void wxMDIParentFrame::OnActivate(wxActivateEvent& event) +{ + event.Skip(); +} + +// Returns the active MDI child window +wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const +{ + return m_currentChild ; +} + +// Create the client window class (don't Create the window, +// just return a new class) +wxMDIClientWindow *wxMDIParentFrame::OnCreateClient() +{ + return new wxMDIClientWindow( this ); +} + +// Responds to colour changes, and passes event on to children. +void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent& event) +{ + // TODO + + // Propagate the event to the non-top-level children + wxFrame::OnSysColourChanged(event); +} + +// MDI operations +void wxMDIParentFrame::Cascade() +{ + // TODO +} + +void wxMDIParentFrame::Tile(wxOrientation WXUNUSED(orient)) +{ + // TODO +} + +void wxMDIParentFrame::ArrangeIcons() +{ + // TODO +} + +void wxMDIParentFrame::ActivateNext() +{ + // TODO +} + +void wxMDIParentFrame::ActivatePrevious() +{ + // TODO +} + +bool wxMDIParentFrame::ShouldBeVisible() const +{ + for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + node; + node = node->GetNext() ) + { + wxWindow *win = node->GetData(); + + if ( win->IsShown() + && !wxDynamicCast(win, wxMDIChildFrame) +#if wxUSE_STATUSBAR + && win != (wxWindow*) GetStatusBar() +#endif + && win != GetClientWindow() ) + { + // if we have a non-MDI child, do remain visible so that it could + // be used + return true; + } + } + + return false; +} + +bool wxMDIParentFrame::Show( bool show ) +{ + m_shouldBeShown = false; + + // don't really show the MDI frame unless it has any children other than + // MDI children as it is pretty useless in this case + + if ( show ) + { + if ( !ShouldBeVisible() && m_currentChild ) + { + // don't make the window visible now but remember that we should + // have had done it + m_shouldBeShown = true; + + return false; + } + } + + return wxFrame::Show(show); +} + +// ---------------------------------------------------------------------------- +// Child frame +// ---------------------------------------------------------------------------- + +wxMDIChildFrame::wxMDIChildFrame() +{ + Init() ; +} +void wxMDIChildFrame::Init() +{ +} + +bool wxMDIChildFrame::Create(wxMDIParentFrame *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) +{ + SetName(name); + + if ( id == wxID_ANY ) + m_windowId = (int)NewControlId(); + else + m_windowId = id; + + if (parent) + parent->AddChild(this); + + MacCreateRealWindow( pos , size , MacRemoveBordersFromStyle(style) , name ) ; + SetTitle( title ); + + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE)); + + return true; +} + +wxMDIChildFrame::~wxMDIChildFrame() +{ + DestroyChildren(); +} + +void wxMDIChildFrame::SetMenuBar(wxMenuBar *menu_bar) +{ + return wxFrame::SetMenuBar( menu_bar ) ; +} + +void wxMDIChildFrame::MacActivate(long timestamp, bool activating) +{ + wxLogTrace(TRACE_MDI, wxT("MDI child=%p MacActivate(0x%08lx,%s)"),this, timestamp, activating ? wxT("ACTIV") : wxT("deact")); + + wxMDIParentFrame *mdiparent = wxDynamicCast(m_parent, wxMDIParentFrame); + wxASSERT(mdiparent); + + if (activating) + { + if (s_macDeactivateWindow == m_parent) + { + wxLogTrace(TRACE_MDI, wxT("parent had been scheduled for deactivation, rehighlighting")); + + UMAHighlightAndActivateWindow((WindowRef)s_macDeactivateWindow->MacGetWindowRef(), true); + + wxLogTrace(TRACE_MDI, wxT("finished highliting parent")); + + s_macDeactivateWindow = NULL; + } + else if ((mdiparent->m_currentChild == this) || !s_macDeactivateWindow) + mdiparent->wxFrame::MacActivate(timestamp, activating); + + if (mdiparent->m_currentChild && mdiparent->m_currentChild != this) + mdiparent->m_currentChild->wxFrame::MacActivate(timestamp, false); + mdiparent->m_currentChild = this; + + if (s_macDeactivateWindow == this) + { + wxLogTrace(TRACE_MDI, wxT("Avoided deactivation/activation of this=%p"), this); + + s_macDeactivateWindow = NULL; + } + else + wxFrame::MacActivate(timestamp, activating); + } + else + { + // We were scheduled for deactivation, and now we do it. + if (s_macDeactivateWindow == this) + { + s_macDeactivateWindow = NULL; + wxFrame::MacActivate(timestamp, activating); + if (mdiparent->m_currentChild == this) + mdiparent->wxFrame::MacActivate(timestamp, activating); + } + else // schedule ourselves for deactivation + { + if (s_macDeactivateWindow) + wxLogTrace(TRACE_MDI, wxT("window=%p SHOULD have been deactivated, oh well!"), s_macDeactivateWindow); + wxLogTrace(TRACE_MDI, wxT("Scheduling delayed deactivation")); + + s_macDeactivateWindow = this; + } + } +} + +// MDI operations +void wxMDIChildFrame::Maximize() +{ + wxFrame::Maximize() ; +} + +void wxMDIChildFrame::Restore() +{ + wxFrame::Restore() ; +} + +void wxMDIChildFrame::Activate() +{ + Raise (); +} + +//----------------------------------------------------------------------------- +// wxMDIClientWindow +//----------------------------------------------------------------------------- + +wxMDIClientWindow::wxMDIClientWindow() +{ +} + +wxMDIClientWindow::~wxMDIClientWindow() +{ + DestroyChildren(); +} + +bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style) +{ + if ( !wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style) ) + return false; + + return true; +} + +// Get size *available for subwindows* i.e. excluding menu bar. +void wxMDIClientWindow::DoGetClientSize(int *x, int *y) const +{ + wxDisplaySize( x , y ) ; +} + +// Explicitly call default scroll behaviour +void wxMDIClientWindow::OnScroll(wxScrollEvent& WXUNUSED(event)) +{ +} + +#endif // wxUSE_MDI diff --git a/src/osx/carbon/mediactrl.cpp b/src/osx/carbon/mediactrl.cpp new file mode 100644 index 0000000000..9d525f5b59 --- /dev/null +++ b/src/osx/carbon/mediactrl.cpp @@ -0,0 +1,1243 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/mediactrl.cpp +// Purpose: Built-in Media Backends for Mac +// Author: Ryan Norton +// Modified by: +// Created: 11/07/04 +// RCS-ID: $Id$ +// Copyright: (c) 2004-2006 Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// OK, a casual overseer of this file may wonder why we don't use +// either CreateMovieControl or HIMovieView... +// +// CreateMovieControl +// 1) Need to dispose and create each time a new movie is loaded +// 2) Not that many real advantages +// 3) Progressively buggier in higher OSX versions +// (see main.c of QTCarbonShell sample for details) +// HIMovieView +// 1) Crashes on destruction in ALL cases on quite a few systems! +// (With the only real "alternative" is to simply not +// dispose of it and let it leak...) +// 2) Massive refreshing bugs with its movie controller between +// movies +// +// At one point we had a complete implementation for CreateMovieControl +// and on my (RN) local copy I had one for HIMovieView - but they +// were simply deemed to be too buggy/unuseful. HIMovieView could +// have been useful as well because it uses OpenGL contexts instead +// of GWorlds. Perhaps someday when someone comes out with some +// ingenious workarounds :). +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_MEDIACTRL + +#include "wx/mediactrl.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/timer.h" +#endif + +// uma is for wxMacFSSpec +#include "wx/mac/uma.h" + +// standard QT stuff +#ifndef __DARWIN__ +#include +#include +#include +#else +#include +#endif + +#if !defined(__LP64__) +#define USE_QUICKTIME 1 +#else +#define USE_QUICKTIME 0 +#endif + +#if USE_QUICKTIME + +//--------------------------------------------------------------------------- +// Height and Width of movie controller in the movie control (apple samples) +//--------------------------------------------------------------------------- +#define wxMCWIDTH 320 +#define wxMCHEIGHT 16 + +//=========================================================================== +// BACKEND DECLARATIONS +//=========================================================================== + +//--------------------------------------------------------------------------- +// wxQTMediaBackend +//--------------------------------------------------------------------------- + +class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase +{ +public: + wxQTMediaBackend(); + virtual ~wxQTMediaBackend(); + + virtual bool CreateControl(wxControl* ctrl, wxWindow* parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name); + + virtual bool Load(const wxString& fileName); + virtual bool Load(const wxURI& location); + + virtual bool Play(); + virtual bool Pause(); + virtual bool Stop(); + + virtual wxMediaState GetState(); + + virtual bool SetPosition(wxLongLong where); + virtual wxLongLong GetPosition(); + virtual wxLongLong GetDuration(); + + virtual void Move(int x, int y, int w, int h); + wxSize GetVideoSize() const; + + virtual double GetPlaybackRate(); + virtual bool SetPlaybackRate(double dRate); + + virtual double GetVolume(); + virtual bool SetVolume(double); + + void Cleanup(); + void FinishLoad(); + + virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags); + + virtual wxLongLong GetDownloadProgress(); + virtual wxLongLong GetDownloadTotal(); + + virtual void MacVisibilityChanged(); + + // + // ------ Implementation from now on -------- + // + bool DoPause(); + bool DoStop(); + + void DoLoadBestSize(); + void DoSetControllerVisible(wxMediaCtrlPlayerControls flags); + + wxLongLong GetDataSizeFromStart(TimeValue end); + + Boolean IsQuickTime4Installed(); + void DoNewMovieController(); + + static pascal void PPRMProc( + Movie theMovie, OSErr theErr, void* theRefCon); + + //TODO: Last param actually long - does this work on 64bit machines? + static pascal Boolean MCFilterProc(MovieController theController, + short action, void *params, long refCon); + + static pascal OSStatus WindowEventHandler( + EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, void *inUserData ); + + wxSize m_bestSize; // Original movie size + Movie m_movie; // Movie instance + bool m_bPlaying; // Whether media is playing or not + class wxTimer* m_timer; // Timer for streaming the movie + MovieController m_mc; // MovieController instance + wxMediaCtrlPlayerControls m_interfaceflags; // Saved interface flags + + // Event handlers and UPPs/Callbacks + EventHandlerRef m_windowEventHandler; + EventHandlerUPP m_windowUPP; + + MoviePrePrerollCompleteUPP m_preprerollupp; + MCActionFilterWithRefConUPP m_mcactionupp; + + GWorldPtr m_movieWorld; //Offscreen movie GWorld + + friend class wxQTMediaEvtHandler; + + DECLARE_DYNAMIC_CLASS(wxQTMediaBackend) +}; + +// helper to hijack background erasing for the QT window +class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler +{ +public: + wxQTMediaEvtHandler(wxQTMediaBackend *qtb) + { + m_qtb = qtb; + + qtb->m_ctrl->Connect( + qtb->m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND, + wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground), + NULL, this); + } + + void OnEraseBackground(wxEraseEvent& event); + +private: + wxQTMediaBackend *m_qtb; + + DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler) +}; + +//=========================================================================== +// IMPLEMENTATION +//=========================================================================== + + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxQTMediaBackend +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend) + +//Time between timer calls - this is the Apple recommondation to the TCL +//team I believe +#define MOVIE_DELAY 20 + +//--------------------------------------------------------------------------- +// wxQTMediaLoadTimer +// +// QT, esp. QT for Windows is very picky about how you go about +// async loading. If you were to go through a Windows message loop +// or a MoviesTask or both and then check the movie load state +// it would still return 1000 (loading)... even (pre)prerolling doesn't +// help. However, making a load timer like this works +//--------------------------------------------------------------------------- +class wxQTMediaLoadTimer : public wxTimer +{ +public: + wxQTMediaLoadTimer(wxQTMediaBackend* parent) : + m_parent(parent) {} + + void Notify() + { + ::MCIdle(m_parent->m_mc); + + // kMovieLoadStatePlayable is not enough on MAC: + // it plays, but IsMovieDone might return true (!) + // sure we need to wait until kMovieLoadStatePlaythroughOK + if (::GetMovieLoadState(m_parent->m_movie) >= 20000) + { + m_parent->FinishLoad(); + delete this; + } + } + +protected: + wxQTMediaBackend *m_parent; // Backend pointer +}; + +// -------------------------------------------------------------------------- +// wxQTMediaPlayTimer - Handle Asyncronous Playing +// +// 1) Checks to see if the movie is done, and if not continues +// streaming the movie +// 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of +// the movie. +// -------------------------------------------------------------------------- +class wxQTMediaPlayTimer : public wxTimer +{ +public: + wxQTMediaPlayTimer(wxQTMediaBackend* parent) : + m_parent(parent) {} + + void Notify() + { + // + // OK, a little explaining - basically originally + // we only called MoviesTask if the movie was actually + // playing (not paused or stopped)... this was before + // we realized MoviesTask actually handles repainting + // of the current frame - so if you were to resize + // or something it would previously not redraw that + // portion of the movie. + // + // So now we call MoviesTask always so that it repaints + // correctly. + // + ::MCIdle(m_parent->m_mc); + + // + // Handle the stop event - if the movie has reached + // the end, notify our handler + // + if (::IsMovieDone(m_parent->m_movie)) + { + if ( m_parent->SendStopEvent() ) + { + m_parent->Stop(); + wxASSERT(::GetMoviesError() == noErr); + + m_parent->QueueFinishEvent(); + } + } + } + +protected: + wxQTMediaBackend* m_parent; // Backend pointer +}; + + +//--------------------------------------------------------------------------- +// wxQTMediaBackend Constructor +// +// Sets m_timer to NULL signifying we havn't loaded anything yet +//--------------------------------------------------------------------------- +wxQTMediaBackend::wxQTMediaBackend() + : m_movie(NULL), m_bPlaying(false), m_timer(NULL) + , m_mc(NULL), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE) + , m_preprerollupp(NULL), m_movieWorld(NULL) +{ +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend Destructor +// +// 1) Cleans up the QuickTime movie instance +// 2) Decrements the QuickTime reference counter - if this reaches +// 0, QuickTime shuts down +// 3) Decrements the QuickTime Windows Media Layer reference counter - +// if this reaches 0, QuickTime shuts down the Windows Media Layer +//--------------------------------------------------------------------------- +wxQTMediaBackend::~wxQTMediaBackend() +{ + if (m_movie) + Cleanup(); + + // Cleanup for moviecontroller + if (m_mc) + { + // destroy wxQTMediaEvtHandler we pushed on it + m_ctrl->PopEventHandler(true); + RemoveEventHandler(m_windowEventHandler); + DisposeEventHandlerUPP(m_windowUPP); + + // Dispose of the movie controller + ::DisposeMovieController(m_mc); + m_mc = NULL; + + // Dispose of offscreen GWorld + ::DisposeGWorld(m_movieWorld); + } + + // Note that ExitMovies() is not necessary... + ExitMovies(); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::CreateControl +// +// 1) Intializes QuickTime +// 2) Creates the control window +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::CreateControl( + wxControl* ctrl, + wxWindow* parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + if (!IsQuickTime4Installed()) + return false; + + EnterMovies(); + + wxMediaCtrl* mediactrl = (wxMediaCtrl*)ctrl; + + // + // Create window + // By default wxWindow(s) is created with a border - + // so we need to get rid of those + // + // Since we don't have a child window like most other + // backends, we don't need wxCLIP_CHILDREN + // + if ( !mediactrl->wxControl::Create( + parent, id, pos, size, + wxWindow::MacRemoveBordersFromStyle(style), + validator, name)) + { + return false; + } + +#if wxUSE_VALIDATORS + mediactrl->SetValidator(validator); +#endif + + m_ctrl = mediactrl; + return true; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::IsQuickTime4Installed +// +// Determines whether version 4 of QT is installed +// (Pretty much for Classic only) +//--------------------------------------------------------------------------- +Boolean wxQTMediaBackend::IsQuickTime4Installed() +{ + OSErr error; + long result; + + error = Gestalt(gestaltQuickTime, &result); + return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::Load (file version) +// +// 1) Get an FSSpec from the Windows path name +// 2) Open the movie +// 3) Obtain the movie instance from the movie resource +// 4) Close the movie resource +// 5) Finish loading +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::Load(const wxString& fileName) +{ + if (m_movie) + Cleanup(); + + ::ClearMoviesStickyError(); // clear previous errors so + // GetMoviesStickyError is useful + + OSErr err = noErr; + short movieResFile; + FSSpec sfFile; + + wxMacFilename2FSSpec( fileName, &sfFile ); + if (OpenMovieFile( &sfFile, &movieResFile, fsRdPerm ) != noErr) + return false; + + short movieResID = 0; + Str255 movieName; + + err = NewMovieFromFile( + &m_movie, + movieResFile, + &movieResID, + movieName, + newMovieActive, + NULL); // wasChanged + + // Do not use ::GetMoviesStickyError() here because it returns -2009 + // a.k.a. invalid track on valid mpegs + if (err == noErr && ::GetMoviesError() == noErr) + { + ::CloseMovieFile(movieResFile); + + // Create movie controller/control + DoNewMovieController(); + + FinishLoad(); + return true; + } + + return false; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::Load (URL Version) +// +// 1) Build an escaped URI from location +// 2) Create a handle to store the URI string +// 3) Put the URI string inside the handle +// 4) Make a QuickTime URL data ref from the handle with the URI in it +// 5) Clean up the URI string handle +// 6) Do some prerolling +// 7) Finish Loading +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::Load(const wxURI& location) +{ + if (m_movie) + Cleanup(); + + ::ClearMoviesStickyError(); // clear previous errors so + // GetMoviesStickyError is useful + + wxString theURI = location.BuildURI(); + OSErr err; + + size_t len; + const char* theURIString; + +#if wxUSE_UNICODE + wxCharBuffer buf = wxConvLocal.cWC2MB(theURI.wc_str(), theURI.length(), &len); + theURIString = buf; +#else + theURIString = theURI; + len = theURI.length(); +#endif + + Handle theHandle = ::NewHandleClear(len + 1); + wxASSERT(theHandle); + + ::BlockMoveData(theURIString, *theHandle, len + 1); + + // create the movie from the handle that refers to the URI + err = ::NewMovieFromDataRef( + &m_movie, + newMovieActive | newMovieAsyncOK /* | newMovieIdleImportOK*/, + NULL, theHandle, + URLDataHandlerSubType); + + ::DisposeHandle(theHandle); + + if (err == noErr && ::GetMoviesStickyError() == noErr) + { + // Movie controller resets prerolling, so we must create first + DoNewMovieController(); + + long timeNow; + Fixed playRate; + + timeNow = ::GetMovieTime(m_movie, NULL); + wxASSERT(::GetMoviesError() == noErr); + + playRate = ::GetMoviePreferredRate(m_movie); + wxASSERT(::GetMoviesError() == noErr); + + // + // Note that the callback here is optional, + // but without it PrePrerollMovie can be buggy + // (see Apple ml). Also, some may wonder + // why we need this at all - this is because + // Apple docs say QuickTime streamed movies + // require it if you don't use a Movie Controller, + // which we don't by default. + // + m_preprerollupp = wxQTMediaBackend::PPRMProc; + ::PrePrerollMovie( m_movie, timeNow, playRate, + m_preprerollupp, (void*)this); + + return true; + } + + return false; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::DoNewMovieController +// +// Attaches movie to moviecontroller or creates moviecontroller +// if not created yet +//--------------------------------------------------------------------------- +void wxQTMediaBackend::DoNewMovieController() +{ + if (!m_mc) + { + // Get top level window ref for some mac functions + WindowRef wrTLW = (WindowRef) m_ctrl->MacGetTopLevelWindowRef(); + + // MovieController not set up yet, so we need to create a new one. + // You have to pass a valid movie to NewMovieController, evidently + ::SetMovieGWorld(m_movie, + (CGrafPtr) GetWindowPort(wrTLW), + NULL); + wxASSERT(::GetMoviesError() == noErr); + + Rect bounds = wxMacGetBoundsForControl( + m_ctrl, + m_ctrl->GetPosition(), + m_ctrl->GetSize()); + + m_mc = ::NewMovieController( + m_movie, &bounds, + mcTopLeftMovie | mcNotVisible /* | mcWithFrame */ ); + wxASSERT(::GetMoviesError() == noErr); + + ::MCDoAction(m_mc, 32, (void*)true); // mcActionSetKeysEnabled + wxASSERT(::GetMoviesError() == noErr); + + // Setup a callback so we can tell when the user presses + // play on the player controls + m_mcactionupp = wxQTMediaBackend::MCFilterProc; + ::MCSetActionFilterWithRefCon( m_mc, m_mcactionupp, (long)this ); + wxASSERT(::GetMoviesError() == noErr); + + // Part of a suggestion from Greg Hazel to repaint movie when idle + m_ctrl->PushEventHandler(new wxQTMediaEvtHandler(this)); + + // Create offscreen GWorld for where to "show" when window is hidden + Rect worldRect; + worldRect.left = worldRect.top = 0; + worldRect.right = worldRect.bottom = 1; + ::NewGWorld(&m_movieWorld, 0, &worldRect, NULL, NULL, 0); + + // Catch window messages: + // if we do not do this and if the user clicks the play + // button on the controller, for instance, nothing will happen... + EventTypeSpec theWindowEventTypes[] = + { + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassWindow, kEventWindowUpdate }, + { kEventClassWindow, kEventWindowActivated }, + { kEventClassWindow, kEventWindowDeactivated } + }; + m_windowUPP = + NewEventHandlerUPP( wxQTMediaBackend::WindowEventHandler ); + InstallWindowEventHandler( + wrTLW, + m_windowUPP, + GetEventTypeCount( theWindowEventTypes ), theWindowEventTypes, + this, + &m_windowEventHandler ); + } + else + { + // MovieController already created: + // Just change the movie in it and we're good to go + Point thePoint; + thePoint.h = thePoint.v = 0; + ::MCSetMovie(m_mc, m_movie, + (WindowRef)m_ctrl->MacGetTopLevelWindowRef(), + thePoint); + wxASSERT(::GetMoviesError() == noErr); + } +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::FinishLoad +// +// Performs operations after a movie ready to play/loaded. +//--------------------------------------------------------------------------- +void wxQTMediaBackend::FinishLoad() +{ + // get the real size of the movie + DoLoadBestSize(); + + // show the player controls if the user wants to + if (m_interfaceflags) + DoSetControllerVisible(m_interfaceflags); + + // we want millisecond precision + ::SetMovieTimeScale(m_movie, 1000); + wxASSERT(::GetMoviesError() == noErr); + + // start movie progress timer + m_timer = new wxQTMediaPlayTimer(this); + wxASSERT(m_timer); + m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); + + // send loaded event and refresh size + NotifyMovieLoaded(); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::DoLoadBestSize +// +// Sets the best size of the control from the real size of the movie +//--------------------------------------------------------------------------- +void wxQTMediaBackend::DoLoadBestSize() +{ + // get the real size of the movie + Rect outRect; + ::GetMovieNaturalBoundsRect(m_movie, &outRect); + wxASSERT(::GetMoviesError() == noErr); + + // determine best size + m_bestSize.x = outRect.right - outRect.left; + m_bestSize.y = outRect.bottom - outRect.top; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::Play +// +// Start the QT movie +// (Apple recommends mcActionPrerollAndPlay but that's QT 4.1+) +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::Play() +{ + Fixed fixRate = (Fixed) (wxQTMediaBackend::GetPlaybackRate() * 0x10000); + if (!fixRate) + fixRate = ::GetMoviePreferredRate(m_movie); + + wxASSERT(fixRate != 0); + + if (!m_bPlaying) + ::MCDoAction( m_mc, 8 /* mcActionPlay */, (void*) fixRate); + + bool result = (::GetMoviesError() == noErr); + if (result) + { + m_bPlaying = true; + QueuePlayEvent(); + } + + return result; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::Pause +// +// Stop the movie +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::DoPause() +{ + // Stop the movie A.K.A. ::StopMovie(m_movie); + if (m_bPlaying) + { + ::MCDoAction( m_mc, 8 /*mcActionPlay*/, (void *) 0); + m_bPlaying = false; + return ::GetMoviesError() == noErr; + } + + // already paused + return true; +} + +bool wxQTMediaBackend::Pause() +{ + bool bSuccess = DoPause(); + if (bSuccess) + this->QueuePauseEvent(); + + return bSuccess; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::Stop +// +// 1) Stop the movie +// 2) Seek to the beginning of the movie +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::DoStop() +{ + if (!wxQTMediaBackend::DoPause()) + return false; + + ::GoToBeginningOfMovie(m_movie); + return ::GetMoviesError() == noErr; +} + +bool wxQTMediaBackend::Stop() +{ + bool bSuccess = DoStop(); + if (bSuccess) + QueueStopEvent(); + + return bSuccess; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetPlaybackRate +// +// 1) Get the movie playback rate from ::GetMovieRate +//--------------------------------------------------------------------------- +double wxQTMediaBackend::GetPlaybackRate() +{ + return ( ((double)::GetMovieRate(m_movie)) / 0x10000); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::SetPlaybackRate +// +// 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::SetPlaybackRate(double dRate) +{ + ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000)); + return ::GetMoviesError() == noErr; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::SetPosition +// +// 1) Create a time record struct (TimeRecord) with appropriate values +// 2) Pass struct to SetMovieTime +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::SetPosition(wxLongLong where) +{ + TimeRecord theTimeRecord; + memset(&theTimeRecord, 0, sizeof(TimeRecord)); + theTimeRecord.value.lo = where.GetValue(); + theTimeRecord.scale = ::GetMovieTimeScale(m_movie); + theTimeRecord.base = ::GetMovieTimeBase(m_movie); + ::SetMovieTime(m_movie, &theTimeRecord); + + if (::GetMoviesError() != noErr) + return false; + + return true; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetPosition +// +// Calls GetMovieTime +//--------------------------------------------------------------------------- +wxLongLong wxQTMediaBackend::GetPosition() +{ + return ::GetMovieTime(m_movie, NULL); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetVolume +// +// Gets the volume through GetMovieVolume - which returns a 16 bit short - +// +// +--------+--------+ +// + (1) + (2) + +// +--------+--------+ +// +// (1) first 8 bits are value before decimal +// (2) second 8 bits are value after decimal +// +// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to +// 1 (full gain and sound) +//--------------------------------------------------------------------------- +double wxQTMediaBackend::GetVolume() +{ + short sVolume = ::GetMovieVolume(m_movie); + + if (sVolume & (128 << 8)) //negative - no sound + return 0.0; + + return sVolume / 256.0; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::SetVolume +// +// Sets the volume through SetMovieVolume - which takes a 16 bit short - +// +// +--------+--------+ +// + (1) + (2) + +// +--------+--------+ +// +// (1) first 8 bits are value before decimal +// (2) second 8 bits are value after decimal +// +// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to +// 1 (full gain and sound) +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::SetVolume(double dVolume) +{ + ::SetMovieVolume(m_movie, (short) (dVolume * 256)); + return true; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetDuration +// +// Calls GetMovieDuration +//--------------------------------------------------------------------------- +wxLongLong wxQTMediaBackend::GetDuration() +{ + return ::GetMovieDuration(m_movie); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetState +// +// Determines the current state - the timer keeps track of whether or not +// we are paused or stopped (if the timer is running we are playing) +//--------------------------------------------------------------------------- +wxMediaState wxQTMediaBackend::GetState() +{ + // Could use + // GetMovieActive/IsMovieDone/SetMovieActive + // combo if implemented that way + if (m_bPlaying) + return wxMEDIASTATE_PLAYING; + else if (!m_movie || wxQTMediaBackend::GetPosition() == 0) + return wxMEDIASTATE_STOPPED; + else + return wxMEDIASTATE_PAUSED; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::Cleanup +// +// Diposes of the movie timer, Control if native, and stops and disposes +// of the QT movie +//--------------------------------------------------------------------------- +void wxQTMediaBackend::Cleanup() +{ + m_bPlaying = false; + if (m_timer) + { + delete m_timer; + m_timer = NULL; + } + + // Stop the movie: + // Apple samples with CreateMovieControl typically + // install a event handler and do this on the dispose + // event, but we do it here for simplicity + // (It might keep playing for several seconds after + // control destruction if not) + wxQTMediaBackend::Pause(); + + // Dispose of control or remove movie from MovieController + Point thePoint; + thePoint.h = thePoint.v = 0; + ::MCSetVisible(m_mc, false); + ::MCSetMovie(m_mc, NULL, NULL, thePoint); + + ::DisposeMovie(m_movie); + m_movie = NULL; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetVideoSize +// +// Returns the actual size of the QT movie +//--------------------------------------------------------------------------- +wxSize wxQTMediaBackend::GetVideoSize() const +{ + return m_bestSize; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::Move +// +// Move the movie controller or movie control +// (we need to actually move the movie control manually...) +// Top 10 things to do with quicktime in March 93's issue +// of DEVELOP - very useful +// http:// www.mactech.com/articles/develop/issue_13/031-033_QuickTime_column.html +// OLD NOTE: Calling MCSetControllerBoundsRect without detaching +// supposively resulted in a crash back then. Current code even +// with CFM classic runs fine. If there is ever a problem, +// take out the if 0 lines below +//--------------------------------------------------------------------------- +void wxQTMediaBackend::Move(int x, int y, int w, int h) +{ + if (m_timer) + { + m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y); + Rect theRect = {y, x, y + h, x + w}; + +#if 0 // see note above + ::MCSetControllerAttached(m_mc, false); + wxASSERT(::GetMoviesError() == noErr); +#endif + + ::MCSetControllerBoundsRect(m_mc, &theRect); + wxASSERT(::GetMoviesError() == noErr); + +#if 0 // see note above + if (m_interfaceflags) + { + ::MCSetVisible(m_mc, true); + wxASSERT(::GetMoviesError() == noErr); + } +#endif + } +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::DoSetControllerVisible +// +// Utility function that takes care of showing the moviecontroller +// and showing/hiding the particular controls on it +//--------------------------------------------------------------------------- +void wxQTMediaBackend::DoSetControllerVisible( + wxMediaCtrlPlayerControls flags) +{ + ::MCSetVisible(m_mc, true); + + // Take care of subcontrols + if (::GetMoviesError() == noErr) + { + long mcFlags = 0; + ::MCDoAction(m_mc, 39/*mcActionGetFlags*/, (void*)&mcFlags); + + if (::GetMoviesError() == noErr) + { + mcFlags |= ( //(1<<0)/*mcFlagSuppressMovieFrame*/ | + (1 << 3)/*mcFlagsUseWindowPalette*/ + | ((flags & wxMEDIACTRLPLAYERCONTROLS_STEP) + ? 0 : (1 << 1)/*mcFlagSuppressStepButtons*/) + | ((flags & wxMEDIACTRLPLAYERCONTROLS_VOLUME) + ? 0 : (1 << 2)/*mcFlagSuppressSpeakerButton*/) + //if we take care of repainting ourselves + // | (1 << 4) /*mcFlagDontInvalidate*/ + ); + + ::MCDoAction(m_mc, 38/*mcActionSetFlags*/, (void*)mcFlags); + } + } + + // Adjust height and width of best size for movie controller + // if the user wants it shown + m_bestSize.x = m_bestSize.x > wxMCWIDTH ? m_bestSize.x : wxMCWIDTH; + m_bestSize.y += wxMCHEIGHT; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::ShowPlayerControls +// +// Shows/Hides subcontrols on the media control +//--------------------------------------------------------------------------- +bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags) +{ + if (!m_mc) + return false; // no movie controller... + + bool bSizeChanged = false; + + // if the controller is visible and we want to hide it do so + if (m_interfaceflags && !flags) + { + bSizeChanged = true; + DoLoadBestSize(); + ::MCSetVisible(m_mc, false); + } + else if (!m_interfaceflags && flags) // show controller if hidden + { + bSizeChanged = true; + DoSetControllerVisible(flags); + } + + // readjust parent sizers + if (bSizeChanged) + { + NotifyMovieSizeChanged(); + + // remember state in case of loading new media + m_interfaceflags = flags; + } + + return ::GetMoviesError() == noErr; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetDataSizeFromStart +// +// Calls either GetMovieDataSize or GetMovieDataSize64 with a value +// of 0 for the starting value +//--------------------------------------------------------------------------- +wxLongLong wxQTMediaBackend::GetDataSizeFromStart(TimeValue end) +{ +#if 0 // old pre-qt4 way + return ::GetMovieDataSize(m_movie, 0, end) +#else // qt4 way + wide llDataSize; + ::GetMovieDataSize64(m_movie, 0, end, &llDataSize); + return wxLongLong(llDataSize.hi, llDataSize.lo); +#endif +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetDownloadProgress +//--------------------------------------------------------------------------- +wxLongLong wxQTMediaBackend::GetDownloadProgress() +{ +#if 0 // hackish and slow + Handle hMovie = NewHandle(0); + PutMovieIntoHandle(m_movie, hMovie); + long lSize = GetHandleSize(hMovie); + DisposeHandle(hMovie); + + return lSize; +#else + TimeValue tv; + if (::GetMaxLoadedTimeInMovie(m_movie, &tv) != noErr) + { + wxLogDebug(wxT("GetMaxLoadedTimeInMovie failed")); + return 0; + } + + return wxQTMediaBackend::GetDataSizeFromStart(tv); +#endif +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::GetDownloadTotal +//--------------------------------------------------------------------------- +wxLongLong wxQTMediaBackend::GetDownloadTotal() +{ + return wxQTMediaBackend::GetDataSizeFromStart( + ::GetMovieDuration(m_movie) + ); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::MacVisibilityChanged +// +// The main problem here is that Windows quicktime, for example, +// renders more directly to a HWND. Mac quicktime does not do this +// and instead renders to the port of the WindowRef/WindowPtr on top +// of everything else/all other windows. +// +// So, for example, if you were to have a CreateTabsControl/wxNotebook +// and change pages, even if you called HIViewSetVisible/SetControlVisibility +// directly the movie will still continue playing on top of everything else +// if you went to a different tab. +// +// Note that another issue, and why we call MCSetControllerPort instead +// of SetMovieGWorld directly, is that in addition to rendering on +// top of everything else the last created controller steals mouse and +// other input from everything else in the window, including other +// controllers. Setting the port of it releases this behaviour. +//--------------------------------------------------------------------------- +void wxQTMediaBackend::MacVisibilityChanged() +{ + if(!m_mc || !m_ctrl->m_bLoaded) + return; //not initialized yet + + if(m_ctrl->IsShownOnScreen()) + { + //The window is being shown again, so set the GWorld of the + //controller back to the port of the parent WindowRef + WindowRef wrTLW = + (WindowRef) m_ctrl->MacGetTopLevelWindowRef(); + + ::MCSetControllerPort(m_mc, (CGrafPtr) GetWindowPort(wrTLW)); + wxASSERT(::GetMoviesError() == noErr); + } + else + { + //We are being hidden - set the GWorld of the controller + //to the offscreen GWorld + ::MCSetControllerPort(m_mc, m_movieWorld); + wxASSERT(::GetMoviesError() == noErr); + } +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::OnEraseBackground +// +// Suggestion from Greg Hazel to repaint the movie when idle +// (on pause also) +//--------------------------------------------------------------------------- +void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent& WXUNUSED(evt)) +{ + // Work around Nasty OSX drawing bug: + // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html + WindowRef wrTLW = (WindowRef) m_qtb->m_ctrl->MacGetTopLevelWindowRef(); + + RgnHandle region = ::MCGetControllerBoundsRgn(m_qtb->m_mc); + ::MCInvalidate(m_qtb->m_mc, wrTLW, region); + ::MCIdle(m_qtb->m_mc); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::PPRMProc (static) +// +// Called when done PrePrerolling the movie. +// Note that in 99% of the cases this does nothing... +// Anyway we set up the loading timer here to tell us when the movie is done +//--------------------------------------------------------------------------- +pascal void wxQTMediaBackend::PPRMProc( + Movie theMovie, + OSErr WXUNUSED_UNLESS_DEBUG(theErr), + void* theRefCon) +{ + wxASSERT( theMovie ); + wxASSERT( theRefCon ); + wxASSERT( theErr == noErr ); + + wxQTMediaBackend* pBE = (wxQTMediaBackend*) theRefCon; + + long lTime = ::GetMovieTime(theMovie,NULL); + Fixed rate = ::GetMoviePreferredRate(theMovie); + ::PrerollMovie(theMovie,lTime,rate); + pBE->m_timer = new wxQTMediaLoadTimer(pBE); + pBE->m_timer->Start(MOVIE_DELAY); +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::MCFilterProc (static) +// +// Callback for when the movie controller recieves a message +//--------------------------------------------------------------------------- +pascal Boolean wxQTMediaBackend::MCFilterProc( + MovieController WXUNUSED(theController), + short action, + void * WXUNUSED(params), + long refCon) +{ + wxQTMediaBackend* pThis = (wxQTMediaBackend*)refCon; + + switch (action) + { + case 1: + // don't process idle events + break; + + case 8: + // play button triggered - MC will set movie to opposite state + // of current - playing ? paused : playing + pThis->m_bPlaying = !(pThis->m_bPlaying); + break; + + default: + break; + } + + return 0; +} + +//--------------------------------------------------------------------------- +// wxQTMediaBackend::WindowEventHandler [static] +// +// Event callback for the top level window of our control that passes +// messages to our moviecontroller so it can receive mouse clicks etc. +//--------------------------------------------------------------------------- +pascal OSStatus wxQTMediaBackend::WindowEventHandler( + EventHandlerCallRef WXUNUSED(inHandlerCallRef), + EventRef inEvent, + void *inUserData) +{ + wxQTMediaBackend* be = (wxQTMediaBackend*) inUserData; + + // Only process keyboard messages on this window if it actually + // has focus, otherwise it will steal keystrokes from other windows! + // As well as when it is not loaded properly as it + // will crash in MCIsPlayerEvent + if((GetEventClass(inEvent) == kEventClassKeyboard && + wxWindow::FindFocus() != be->m_ctrl) + || !be->m_ctrl->m_bLoaded) + return eventNotHandledErr; + + // Pass the event onto the movie controller + EventRecord theEvent; + ConvertEventRefToEventRecord( inEvent, &theEvent ); + OSStatus err; + + // TODO: Apple says MCIsPlayerEvent is depreciated and + // MCClick, MCKey, MCIdle etc. should be used + // (RN: Of course that's what they say about + // CreateMovieControl and HIMovieView as well, LOL!) + err = ::MCIsPlayerEvent( be->m_mc, &theEvent ); + + // Pass on to other event handlers if not handled- i.e. wx + if (err != noErr) + return noErr; + else + return eventNotHandledErr; +} + +#endif + +// in source file that contains stuff you don't directly use +#include "wx/html/forcelnk.h" +FORCE_LINK_ME(basewxmediabackends) + +#endif // wxUSE_MEDIACTRL diff --git a/src/osx/carbon/menu.cpp b/src/osx/carbon/menu.cpp new file mode 100644 index 0000000000..9311f4fdb6 --- /dev/null +++ b/src/osx/carbon/menu.cpp @@ -0,0 +1,1183 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/menu.cpp +// Purpose: wxMenu, wxMenuBar, wxMenuItem +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// headers & declarations +// ============================================================================ + +// wxWidgets headers +// ----------------- + +#include "wx/wxprec.h" + +#include "wx/menu.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/frame.h" + #include "wx/menuitem.h" +#endif + +#include "wx/mac/uma.h" + +// other standard headers +// ---------------------- +#include + +IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler) +IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler) + +// the (popup) menu title has this special id +static const int idMenuTitle = -3; + +static const short kwxMacAppleMenuId = 1 ; + + +// Find an item given the Macintosh Menu Reference + +WX_DECLARE_HASH_MAP(MenuRef, wxMenu*, wxPointerHash, wxPointerEqual, MacMenuMap); + +static MacMenuMap wxWinMacMenuList; + +wxMenu *wxFindMenuFromMacMenu(MenuRef inMenuRef) +{ + MacMenuMap::iterator node = wxWinMacMenuList.find(inMenuRef); + + return (node == wxWinMacMenuList.end()) ? NULL : node->second; +} + +void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) ; +void wxAssociateMenuWithMacMenu(MenuRef inMenuRef, wxMenu *menu) +{ + // adding NULL MenuRef is (first) surely a result of an error and + // (secondly) breaks menu command processing + wxCHECK_RET( inMenuRef != (MenuRef) NULL, wxT("attempt to add a NULL MenuRef to menu list") ); + + wxWinMacMenuList[inMenuRef] = menu; +} + +void wxRemoveMacMenuAssociation(wxMenu *menu) ; +void wxRemoveMacMenuAssociation(wxMenu *menu) +{ + // iterate over all the elements in the class + MacMenuMap::iterator it; + for ( it = wxWinMacMenuList.begin(); it != wxWinMacMenuList.end(); ++it ) + { + if ( it->second == menu ) + { + wxWinMacMenuList.erase(it); + break; + } + } +} + +void wxInsertMenuItemsInMenu(wxMenu* menu, MenuRef wm, MenuItemIndex insertAfter) +{ + wxMenuItemList::compatibility_iterator node; + wxMenuItem *item; + wxMenu *subMenu = NULL ; + bool newItems = false; + + for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext()) + { + item = (wxMenuItem *)node->GetData(); + subMenu = item->GetSubMenu() ; + if (subMenu) + { + wxInsertMenuItemsInMenu(subMenu, (MenuRef)subMenu->GetHMenu(), 0); + } + if ( item->IsSeparator() ) + { + if ( wm && newItems) + InsertMenuItemTextWithCFString( wm, + CFSTR(""), insertAfter, kMenuItemAttrSeparator, 0); + + newItems = false; + } + else + { + wxAcceleratorEntry* + entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ; + + MenuItemIndex winListPos = (MenuItemIndex)-1; + OSStatus err = GetIndMenuItemWithCommandID(wm, + wxIdToMacCommand ( item->GetId() ), 1, NULL, &winListPos); + + if ( wm && err == menuItemNotFoundErr ) + { + // NB: the only way to determine whether or not we should add + // a separator is to know if we've added menu items to the menu + // before the separator. + newItems = true; + UMAInsertMenuItem(wm, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), insertAfter, entry); + SetMenuItemCommandID( wm , insertAfter+1 , wxIdToMacCommand ( item->GetId() ) ) ; + SetMenuItemRefCon( wm , insertAfter+1 , (URefCon) item ) ; + } + + delete entry ; + } + } +} + +// ============================================================================ +// implementation +// ============================================================================ +static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) ; +static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win ); + +// Menus + +// Construct a menu with optional title (then use append) + +#ifdef __DARWIN__ +short wxMenu::s_macNextMenuId = 3 ; +#else +short wxMenu::s_macNextMenuId = 2 ; +#endif + +static +wxMenu * +_wxMenuAt(const wxMenuList &menuList, size_t pos) +{ + wxMenuList::compatibility_iterator menuIter = menuList.GetFirst(); + + while (pos-- > 0) + menuIter = menuIter->GetNext(); + + return menuIter->GetData() ; +} + +void wxMenu::Init() +{ + m_doBreak = false; + m_startRadioGroup = -1; + + // create the menu + m_macMenuId = s_macNextMenuId++; + m_hMenu = UMANewMenu(m_macMenuId, m_title, wxFont::GetDefaultEncoding() ); + + if ( !m_hMenu ) + { + wxLogLastError(wxT("UMANewMenu failed")); + } + + wxAssociateMenuWithMacMenu( (MenuRef)m_hMenu , this ) ; + + // if we have a title, insert it in the beginning of the menu + if ( !m_title.empty() ) + { + Append(idMenuTitle, m_title) ; + AppendSeparator() ; + } +} + +wxMenu::~wxMenu() +{ + wxRemoveMacMenuAssociation( this ) ; + if (MAC_WXHMENU(m_hMenu)) + ::DisposeMenu(MAC_WXHMENU(m_hMenu)); +} + +void wxMenu::Break() +{ + // not available on the mac platform +} + +void wxMenu::Attach(wxMenuBarBase *menubar) +{ + wxMenuBase::Attach(menubar); + + EndRadioGroup(); +} + +// function appends a new item or submenu to the menu +// append a new item or submenu to the menu +bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) +{ + wxASSERT_MSG( pItem != NULL, wxT("can't append NULL item to the menu") ); + + if ( pItem->IsSeparator() ) + { + if ( pos == (size_t)-1 ) + AppendMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu), + CFSTR(""), kMenuItemAttrSeparator, 0,NULL); + else + InsertMenuItemTextWithCFString( MAC_WXHMENU(m_hMenu), + CFSTR(""), pos, kMenuItemAttrSeparator, 0); + } + else + { + wxMenu *pSubMenu = pItem->GetSubMenu() ; + if ( pSubMenu != NULL ) + { + wxASSERT_MSG( pSubMenu->m_hMenu != NULL , wxT("invalid submenu added")); + pSubMenu->m_menuParent = this ; + + // We need the !GetMenuBar() check to make sure we run MacBeforeDisplay() + // for popup menus and other menus which may not be part of the main + // menu bar. + if (!GetMenuBar() || wxMenuBar::MacGetInstalledMenuBar() == GetMenuBar()) + pSubMenu->MacBeforeDisplay( true ) ; + + if ( pos == (size_t)-1 ) + UMAAppendSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetItemLabel()), wxFont::GetDefaultEncoding(), pSubMenu->m_macMenuId); + else + UMAInsertSubMenuItem(MAC_WXHMENU(m_hMenu), wxStripMenuCodes(pItem->GetItemLabel()), wxFont::GetDefaultEncoding(), pos, pSubMenu->m_macMenuId); + + pItem->UpdateItemBitmap() ; + pItem->UpdateItemStatus() ; + } + else + { + if ( pos == (size_t)-1 ) + { + UMAAppendMenuItem(MAC_WXHMENU(m_hMenu), wxT("a") , wxFont::GetDefaultEncoding() ); + pos = CountMenuItems(MAC_WXHMENU(m_hMenu)) ; + } + else + { + // MacOS counts menu items from 1 and inserts after, therefore having the + // same effect as wx 0 based and inserting before, we must correct pos + // after however for updates to be correct + UMAInsertMenuItem(MAC_WXHMENU(m_hMenu), wxT("a"), wxFont::GetDefaultEncoding(), pos); + pos += 1 ; + } + + SetMenuItemCommandID( MAC_WXHMENU(m_hMenu) , pos , wxIdToMacCommand ( pItem->GetId() ) ) ; + SetMenuItemRefCon( MAC_WXHMENU(m_hMenu) , pos , (URefCon) pItem ) ; + pItem->UpdateItemText() ; + pItem->UpdateItemBitmap() ; + pItem->UpdateItemStatus() ; + + if ( pItem->GetId() == idMenuTitle ) + UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , pos , false ) ; + } + } + + // if we're already attached to the menubar, we must update it + if ( IsAttached() && GetMenuBar()->IsAttached() ) + GetMenuBar()->Refresh(); + + return true ; +} + +void wxMenu::EndRadioGroup() +{ + // we're not inside a radio group any longer + m_startRadioGroup = -1; +} + +wxMenuItem* wxMenu::DoAppend(wxMenuItem *item) +{ + wxCHECK_MSG( item, NULL, _T("NULL item in wxMenu::DoAppend") ); + + bool check = false; + + if ( item->GetKind() == wxITEM_RADIO ) + { + int count = GetMenuItemCount(); + + if ( m_startRadioGroup == -1 ) + { + // start a new radio group + m_startRadioGroup = count; + + // for now it has just one element + item->SetAsRadioGroupStart(); + item->SetRadioGroupEnd(m_startRadioGroup); + + // ensure that we have a checked item in the radio group + check = true; + } + else // extend the current radio group + { + // we need to update its end item + item->SetRadioGroupStart(m_startRadioGroup); + wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup); + + if ( node ) + { + node->GetData()->SetRadioGroupEnd(count); + } + else + { + wxFAIL_MSG( _T("where is the radio group start item?") ); + } + } + } + else // not a radio item + { + EndRadioGroup(); + } + + if ( !wxMenuBase::DoAppend(item) || !DoInsertOrAppend(item) ) + return NULL; + + if ( check ) + // check the item initially + item->Check(true); + + return item; +} + +wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item) +{ + if (wxMenuBase::DoInsert(pos, item) && DoInsertOrAppend(item, pos)) + return item; + + return NULL; +} + +wxMenuItem *wxMenu::DoRemove(wxMenuItem *item) +{ + // we need to find the items position in the child list + size_t pos; + wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); + + for ( pos = 0; node; pos++ ) + { + if ( node->GetData() == item ) + break; + + node = node->GetNext(); + } + + // DoRemove() (unlike Remove) can only be called for existing item! + wxCHECK_MSG( node, NULL, wxT("bug in wxMenu::Remove logic") ); + + ::DeleteMenuItem(MAC_WXHMENU(m_hMenu) , pos + 1); + + if ( IsAttached() && GetMenuBar()->IsAttached() ) + // otherwise, the change won't be visible + GetMenuBar()->Refresh(); + + // and from internal data structures + return wxMenuBase::DoRemove(item); +} + +void wxMenu::SetTitle(const wxString& label) +{ + m_title = label ; + UMASetMenuTitle(MAC_WXHMENU(m_hMenu) , label , wxFont::GetDefaultEncoding() ) ; +} + +bool wxMenu::ProcessCommand(wxCommandEvent & event) +{ + bool processed = false; + + // Try the menu's event handler + if ( /* !processed && */ GetEventHandler()) + processed = GetEventHandler()->SafelyProcessEvent(event); + + // Try the window the menu was popped up from + // (and up through the hierarchy) + wxWindow *win = GetInvokingWindow(); + if ( !processed && win ) + processed = win->HandleWindowEvent(event); + + return processed; +} + +// --------------------------------------------------------------------------- +// other +// --------------------------------------------------------------------------- + +wxWindow *wxMenu::GetWindow() const +{ + if ( m_invokingWindow != NULL ) + return m_invokingWindow; + else if ( GetMenuBar() != NULL) + return (wxWindow *) GetMenuBar()->GetFrame(); + + return NULL; +} + +// helper functions returning the mac menu position for a certain item, note that this is +// mac-wise 1 - based, i.e. the first item has index 1 whereas on MSWin it has pos 0 + +int wxMenu::MacGetIndexFromId( int id ) +{ + size_t pos; + wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); + for ( pos = 0; node; pos++ ) + { + if ( node->GetData()->GetId() == id ) + break; + + node = node->GetNext(); + } + + if (!node) + return 0; + + return pos + 1 ; +} + +int wxMenu::MacGetIndexFromItem( wxMenuItem *pItem ) +{ + size_t pos; + wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); + for ( pos = 0; node; pos++ ) + { + if ( node->GetData() == pItem ) + break; + + node = node->GetNext(); + } + + if (!node) + return 0; + + return pos + 1 ; +} + +void wxMenu::MacEnableMenu( bool bDoEnable ) +{ + UMAEnableMenuItem(MAC_WXHMENU(m_hMenu) , 0 , bDoEnable ) ; + + ::DrawMenuBar() ; +} + +// MacOS needs to know about submenus somewhere within this menu +// before it can be displayed, also hide special menu items +// like preferences that are handled by the OS +void wxMenu::MacBeforeDisplay( bool isSubMenu ) +{ + wxMenuItem* previousItem = NULL ; + size_t pos ; + wxMenuItemList::compatibility_iterator node; + wxMenuItem *item; + + for (pos = 0, node = GetMenuItems().GetFirst(); node; node = node->GetNext(), pos++) + { + item = (wxMenuItem *)node->GetData(); + wxMenu* subMenu = item->GetSubMenu() ; + if (subMenu) + { + subMenu->MacBeforeDisplay( true ) ; + } + else // normal item + { + // what we do here is to hide the special items which are + // shown in the application menu anyhow -- it doesn't make + // sense to show them in their normal place as well + if ( item->GetId() == wxApp::s_macAboutMenuItemId || + item->GetId() == wxApp::s_macPreferencesMenuItemId || + item->GetId() == wxApp::s_macExitMenuItemId ) + + { + ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ), + pos + 1, kMenuItemAttrHidden, 0 ); + + // also check for a separator which was used just to + // separate this item from the others, so don't leave + // separator at the menu start or end nor 2 consecutive + // separators + wxMenuItemList::compatibility_iterator nextNode = node->GetNext(); + wxMenuItem *next = nextNode ? nextNode->GetData() : NULL; + + size_t posSeptoHide; + if ( !previousItem && next && next->IsSeparator() ) + { + // next (i.e. second as we must be first) item is + // the separator to hide + wxASSERT_MSG( pos == 0, _T("should be the menu start") ); + posSeptoHide = 2; + } + else if ( GetMenuItems().GetCount() == pos + 1 && + previousItem != NULL && + previousItem->IsSeparator() ) + { + // prev item is a trailing separator we want to hide + posSeptoHide = pos; + } + else if ( previousItem && previousItem->IsSeparator() && + next && next->IsSeparator() ) + { + // two consecutive separators, this is one too many + posSeptoHide = pos; + } + else // no separators to hide + { + posSeptoHide = 0; + } + + if ( posSeptoHide ) + { + // hide the separator as well + ChangeMenuItemAttributes( MAC_WXHMENU( GetHMenu() ), + posSeptoHide, + kMenuItemAttrHidden, + 0 ); + } + } + } + + previousItem = item ; + } + + if ( isSubMenu ) + ::InsertMenu(MAC_WXHMENU( GetHMenu()), -1); +} + +// undo all changes from the MacBeforeDisplay call +void wxMenu::MacAfterDisplay( bool isSubMenu ) +{ + if ( isSubMenu ) + ::DeleteMenu(MacGetMenuId()); + + wxMenuItemList::compatibility_iterator node; + wxMenuItem *item; + + for (node = GetMenuItems().GetFirst(); node; node = node->GetNext()) + { + item = (wxMenuItem *)node->GetData(); + wxMenu* subMenu = item->GetSubMenu() ; + if (subMenu) + { + subMenu->MacAfterDisplay( true ) ; + } + else + { + // no need to undo hidings + } + } +} + +wxInt32 wxMenu::MacHandleCommandProcess( wxMenuItem* item, int id, wxWindow* targetWindow ) +{ + OSStatus result = eventNotHandledErr ; + if (item->IsCheckable()) + item->Check( !item->IsChecked() ) ; + + if ( SendEvent( id , item->IsCheckable() ? item->IsChecked() : -1 ) ) + result = noErr ; + else + { + if ( targetWindow != NULL ) + { + wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED , id); + event.SetEventObject(targetWindow); + event.SetInt(item->IsCheckable() ? item->IsChecked() : -1); + + if ( targetWindow->HandleWindowEvent(event) ) + result = noErr ; + } + } + return result; +} + +wxInt32 wxMenu::MacHandleCommandUpdateStatus(wxMenuItem* WXUNUSED(item), + int id, + wxWindow* targetWindow) +{ + OSStatus result = eventNotHandledErr ; + wxUpdateUIEvent event(id); + event.SetEventObject( this ); + + bool processed = false; + + // Try the menu's event handler + { + wxEvtHandler *handler = GetEventHandler(); + if ( handler ) + processed = handler->ProcessEvent(event); + } + + // Try the window the menu was popped up from + // (and up through the hierarchy) + if ( !processed ) + { + const wxMenuBase *menu = this; + while ( menu ) + { + wxWindow *win = menu->GetInvokingWindow(); + if ( win ) + { + processed = win->HandleWindowEvent(event); + break; + } + + menu = menu->GetParent(); + } + } + + if ( !processed && targetWindow != NULL) + { + processed = targetWindow->HandleWindowEvent(event); + } + + if ( processed ) + { + // if anything changed, update the changed attribute + if (event.GetSetText()) + SetLabel(id, event.GetText()); + if (event.GetSetChecked()) + Check(id, event.GetChecked()); + if (event.GetSetEnabled()) + Enable(id, event.GetEnabled()); + + result = noErr ; + } + return result; +} + +// Menu Bar + +/* + +Mac Implementation note : + +The Mac has only one global menubar, so we attempt to install the currently +active menubar from a frame, we currently don't take into account mdi-frames +which would ask for menu-merging + +Secondly there is no mac api for changing a menubar that is not the current +menubar, so we have to wait for preparing the actual menubar until the +wxMenubar is to be used + +We can in subsequent versions use MacInstallMenuBar to provide some sort of +auto-merge for MDI in case this will be necessary + +*/ + +wxMenuBar* wxMenuBar::s_macInstalledMenuBar = NULL ; +wxMenuBar* wxMenuBar::s_macCommonMenuBar = NULL ; +bool wxMenuBar::s_macAutoWindowMenu = true ; +WXHMENU wxMenuBar::s_macWindowMenuHandle = NULL ; + +void wxMenuBar::Init() +{ + m_eventHandler = this; + m_menuBarFrame = NULL; + m_invokingWindow = (wxWindow*) NULL; +} + +wxMenuBar::wxMenuBar() +{ + Init(); +} + +wxMenuBar::wxMenuBar( long WXUNUSED(style) ) +{ + Init(); +} + +wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED(style)) +{ + Init(); + + m_titles.Alloc(count); + + for ( size_t i = 0; i < count; i++ ) + { + m_menus.Append(menus[i]); + m_titles.Add(titles[i]); + + menus[i]->Attach(this); + } +} + +wxMenuBar::~wxMenuBar() +{ + if (s_macCommonMenuBar == this) + s_macCommonMenuBar = NULL; + + if (s_macInstalledMenuBar == this) + { + ::ClearMenuBar(); + s_macInstalledMenuBar = NULL; + } +} + +void wxMenuBar::Refresh(bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect)) +{ + wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") ); + + DrawMenuBar(); +} + +void wxMenuBar::MacInstallMenuBar() +{ + if ( s_macInstalledMenuBar == this ) + return ; + + MenuBarHandle menubar = NULL ; + + menubar = NewHandleClear( 6 /* sizeof( MenuBarHeader ) */ ) ; + + ::SetMenuBar( menubar ) ; + DisposeMenuBar( menubar ) ; + MenuHandle appleMenu = NULL ; + + verify_noerr( CreateNewMenu( kwxMacAppleMenuId , 0 , &appleMenu ) ) ; + verify_noerr( SetMenuTitleWithCFString( appleMenu , CFSTR( "\x14" ) ) ); + + // Add About/Preferences separator only on OS X + // KH/RN: Separator is always present on 10.3 but not on 10.2 + // However, the change from 10.2 to 10.3 suggests it is preferred + InsertMenuItemTextWithCFString( appleMenu, + CFSTR(""), 0, kMenuItemAttrSeparator, 0); + InsertMenuItemTextWithCFString( appleMenu, + CFSTR("About..."), 0, 0, 0); + MacInsertMenu( appleMenu , 0 ) ; + + // if we have a mac help menu, clean it up before adding new items + MenuHandle helpMenuHandle ; + MenuItemIndex firstUserHelpMenuItem ; + + if ( UMAGetHelpMenuDontCreate( &helpMenuHandle , &firstUserHelpMenuItem) == noErr ) + { + for ( int i = CountMenuItems( helpMenuHandle ) ; i >= firstUserHelpMenuItem ; --i ) + DeleteMenuItem( helpMenuHandle , i ) ; + } + else + { + helpMenuHandle = NULL ; + } + + if ( wxApp::s_macPreferencesMenuItemId) + { + wxMenuItem *item = FindItem( wxApp::s_macPreferencesMenuItemId , NULL ) ; + if ( item == NULL || !(item->IsEnabled()) ) + DisableMenuCommand( NULL , kHICommandPreferences ) ; + else + EnableMenuCommand( NULL , kHICommandPreferences ) ; + } + + // Unlike preferences which may or may not exist, the Quit item should be always + // enabled unless it is added by the application and then disabled, otherwise + // a program would be required to add an item with wxID_EXIT in order to get the + // Quit menu item to be enabled, which seems a bit burdensome. + if ( wxApp::s_macExitMenuItemId) + { + wxMenuItem *item = FindItem( wxApp::s_macExitMenuItemId , NULL ) ; + if ( item != NULL && !(item->IsEnabled()) ) + DisableMenuCommand( NULL , kHICommandQuit ) ; + else + EnableMenuCommand( NULL , kHICommandQuit ) ; + } + + wxString strippedHelpMenuTitle = wxStripMenuCodes( wxApp::s_macHelpMenuTitleName ) ; + wxString strippedTranslatedHelpMenuTitle = wxStripMenuCodes( wxString( _("&Help") ) ) ; + wxMenuList::compatibility_iterator menuIter = m_menus.GetFirst(); + for (size_t i = 0; i < m_menus.GetCount(); i++, menuIter = menuIter->GetNext()) + { + wxMenuItemList::compatibility_iterator node; + wxMenuItem *item; + wxMenu* menu = menuIter->GetData() , *subMenu = NULL ; + wxString strippedMenuTitle = wxStripMenuCodes(m_titles[i]); + + if ( strippedMenuTitle == wxT("?") || strippedMenuTitle == strippedHelpMenuTitle || strippedMenuTitle == strippedTranslatedHelpMenuTitle ) + { + for (node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext()) + { + item = (wxMenuItem *)node->GetData(); + subMenu = item->GetSubMenu() ; + if (subMenu) + { + // we don't support hierarchical menus in the help menu yet + } + else + { + if ( item->GetId() != wxApp::s_macAboutMenuItemId ) + { + // we have found a user help menu and an item other than the about item, + // so we can create the mac help menu now, if we haven't created it yet + if ( helpMenuHandle == NULL ) + { + if ( UMAGetHelpMenu( &helpMenuHandle , &firstUserHelpMenuItem) != noErr ) + { + helpMenuHandle = NULL ; + break ; + } + } + } + + if ( item->IsSeparator() ) + { + if ( helpMenuHandle ) + AppendMenuItemTextWithCFString( helpMenuHandle, + CFSTR(""), kMenuItemAttrSeparator, 0,NULL); + } + else + { + wxAcceleratorEntry* + entry = wxAcceleratorEntry::Create( item->GetItemLabel() ) ; + + if ( item->GetId() == wxApp::s_macAboutMenuItemId ) + { + // this will be taken care of below + } + else + { + if ( helpMenuHandle ) + { + UMAAppendMenuItem(helpMenuHandle, wxStripMenuCodes(item->GetItemLabel()) , wxFont::GetDefaultEncoding(), entry); + SetMenuItemCommandID( helpMenuHandle , CountMenuItems(helpMenuHandle) , wxIdToMacCommand ( item->GetId() ) ) ; + SetMenuItemRefCon( helpMenuHandle , CountMenuItems(helpMenuHandle) , (URefCon) item ) ; + } + } + + delete entry ; + } + } + } + } + + else if ( ( m_titles[i] == wxT("Window") || m_titles[i] == wxT("&Window") ) + && GetAutoWindowMenu() ) + { + if ( MacGetWindowMenuHMenu() == NULL ) + { + CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ; + } + + MenuRef wm = (MenuRef)MacGetWindowMenuHMenu(); + if ( wm == NULL ) + break; + + // get the insertion point in the standard menu + MenuItemIndex winListStart; + GetIndMenuItemWithCommandID(wm, + kHICommandWindowListSeparator, 1, NULL, &winListStart); + + // add a separator so that the standard items and the custom items + // aren't mixed together, but only if this is the first run + OSStatus err = GetIndMenuItemWithCommandID(wm, + 'WXWM', 1, NULL, NULL); + + if ( err == menuItemNotFoundErr ) + { + InsertMenuItemTextWithCFString( wm, + CFSTR(""), winListStart-1, kMenuItemAttrSeparator, 'WXWM'); + } + + wxInsertMenuItemsInMenu(menu, wm, winListStart); + } + else + { + UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , m_titles[i], GetFont().GetEncoding() ) ; + menu->MacBeforeDisplay(false) ; + + ::InsertMenu(MAC_WXHMENU(_wxMenuAt(m_menus, i)->GetHMenu()), 0); + } + } + + // take care of the about menu item wherever it is + { + wxMenu* aboutMenu ; + wxMenuItem *aboutMenuItem = FindItem(wxApp::s_macAboutMenuItemId , &aboutMenu) ; + if ( aboutMenuItem ) + { + wxAcceleratorEntry* + entry = wxAcceleratorEntry::Create( aboutMenuItem->GetItemLabel() ) ; + UMASetMenuItemText( GetMenuHandle( kwxMacAppleMenuId ) , 1 , wxStripMenuCodes ( aboutMenuItem->GetItemLabel() ) , wxFont::GetDefaultEncoding() ); + UMAEnableMenuItem( GetMenuHandle( kwxMacAppleMenuId ) , 1 , true ); + SetMenuItemCommandID( GetMenuHandle( kwxMacAppleMenuId ) , 1 , kHICommandAbout ) ; + SetMenuItemRefCon(GetMenuHandle( kwxMacAppleMenuId ) , 1 , (URefCon)aboutMenuItem ) ; + UMASetMenuItemShortcut( GetMenuHandle( kwxMacAppleMenuId ) , 1 , entry ) ; + } + } + + if ( GetAutoWindowMenu() ) + { + if ( MacGetWindowMenuHMenu() == NULL ) + CreateStandardWindowMenu( 0 , (MenuHandle*) &s_macWindowMenuHandle ) ; + + InsertMenu( (MenuHandle) MacGetWindowMenuHMenu() , 0 ) ; + } + + ::DrawMenuBar() ; + s_macInstalledMenuBar = this; +} + +void wxMenuBar::EnableTop(size_t pos, bool enable) +{ + wxCHECK_RET( IsAttached(), wxT("doesn't work with unattached menubars") ); + + _wxMenuAt(m_menus, pos)->MacEnableMenu( enable ) ; + Refresh(); +} + +bool wxMenuBar::Enable(bool enable) +{ + wxCHECK_MSG( IsAttached(), false, wxT("doesn't work with unattached menubars") ); + + size_t i; + for (i = 0; i < GetMenuCount(); i++) + EnableTop(i, enable); + + return true; +} + +void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label) +{ + wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") ); + + m_titles[pos] = label; + + if ( !IsAttached() ) + return; + + _wxMenuAt(m_menus, pos)->SetTitle( label ) ; + + if (wxMenuBar::s_macInstalledMenuBar == this) // are we currently installed ? + { + ::SetMenuBar( GetMenuBar() ) ; + ::InvalMenuBar() ; + } +} + +wxString wxMenuBar::GetMenuLabel(size_t pos) const +{ + wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString, + wxT("invalid menu index in wxMenuBar::GetMenuLabel") ); + + return m_titles[pos]; +} + +int wxMenuBar::FindMenu(const wxString& title) +{ + wxString menuTitle = wxStripMenuCodes(title); + + size_t count = GetMenuCount(); + for ( size_t i = 0; i < count; i++ ) + { + wxString title = wxStripMenuCodes(m_titles[i]); + if ( menuTitle == title ) + return i; + } + + return wxNOT_FOUND; +} + +// --------------------------------------------------------------------------- +// wxMenuBar construction +// --------------------------------------------------------------------------- + +wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title) +{ + wxMenu *menuOld = wxMenuBarBase::Replace(pos, menu, title); + if ( !menuOld ) + return NULL; + + m_titles[pos] = title; + + if ( IsAttached() ) + { + if (s_macInstalledMenuBar == this) + { + menuOld->MacAfterDisplay( false ) ; + ::DeleteMenu( menuOld->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ; + + menu->MacBeforeDisplay( false ) ; + UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , GetFont().GetEncoding() ) ; + if ( pos == m_menus.GetCount() - 1) + ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ; + else + ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos + 1)->MacGetMenuId() ) ; + } + + Refresh(); + } + + if (m_invokingWindow) + wxMenubarSetInvokingWindow( menu, m_invokingWindow ); + + return menuOld; +} + +bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title) +{ + if ( !wxMenuBarBase::Insert(pos, menu, title) ) + return false; + + m_titles.Insert(title, pos); + + UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , GetFont().GetEncoding() ) ; + + if ( IsAttached() && s_macInstalledMenuBar == this ) + { + if (s_macInstalledMenuBar == this) + { + menu->MacBeforeDisplay( false ) ; + + if ( pos == (size_t) -1 || pos + 1 == m_menus.GetCount() ) + ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ; + else + ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , _wxMenuAt(m_menus, pos+1)->MacGetMenuId() ) ; + } + + Refresh(); + } + + if (m_invokingWindow) + wxMenubarSetInvokingWindow( menu, m_invokingWindow ); + + return true; +} + +wxMenu *wxMenuBar::Remove(size_t pos) +{ + wxMenu *menu = wxMenuBarBase::Remove(pos); + if ( !menu ) + return NULL; + + if ( IsAttached() ) + { + if (s_macInstalledMenuBar == this) + ::DeleteMenu( menu->MacGetMenuId() /* m_menus[pos]->MacGetMenuId() */ ) ; + + Refresh(); + } + + m_titles.RemoveAt(pos); + + return menu; +} + +bool wxMenuBar::Append(wxMenu *menu, const wxString& title) +{ + WXHMENU submenu = menu ? menu->GetHMenu() : 0; + wxCHECK_MSG( submenu, false, wxT("can't append invalid menu to menubar") ); + + if ( !wxMenuBarBase::Append(menu, title) ) + return false; + + m_titles.Add(title); + + UMASetMenuTitle( MAC_WXHMENU(menu->GetHMenu()) , title , GetFont().GetEncoding() ) ; + + if ( IsAttached() ) + { + if (s_macInstalledMenuBar == this) + { + menu->MacBeforeDisplay( false ) ; + ::InsertMenu( MAC_WXHMENU(menu->GetHMenu()) , 0 ) ; + } + + Refresh(); + } + + // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables + // adding menu later on. + if (m_invokingWindow) + wxMenubarSetInvokingWindow( menu, m_invokingWindow ); + + return true; +} + +static void wxMenubarUnsetInvokingWindow( wxMenu *menu ) +{ + menu->SetInvokingWindow( (wxWindow*) NULL ); + wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); + + while (node) + { + wxMenuItem *menuitem = node->GetData(); + if (menuitem->IsSubMenu()) + wxMenubarUnsetInvokingWindow( menuitem->GetSubMenu() ); + + node = node->GetNext(); + } +} + +static void wxMenubarSetInvokingWindow( wxMenu *menu, wxWindow *win ) +{ + menu->SetInvokingWindow( win ); + wxMenuItem *menuitem; + wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); + + while (node) + { + menuitem = node->GetData(); + if (menuitem->IsSubMenu()) + wxMenubarSetInvokingWindow( menuitem->GetSubMenu() , win ); + + node = node->GetNext(); + } +} + +void wxMenuBar::UnsetInvokingWindow() +{ + m_invokingWindow = (wxWindow*) NULL; + wxMenu *menu; + wxMenuList::compatibility_iterator node = m_menus.GetFirst(); + + while (node) + { + menu = node->GetData(); + wxMenubarUnsetInvokingWindow( menu ); + + node = node->GetNext(); + } +} + +void wxMenuBar::SetInvokingWindow(wxFrame *frame) +{ + m_invokingWindow = frame; + wxMenu *menu; + wxMenuList::compatibility_iterator node = m_menus.GetFirst(); + + while (node) + { + menu = node->GetData(); + wxMenubarSetInvokingWindow( menu, frame ); + + node = node->GetNext(); + } +} + +void wxMenuBar::Detach() +{ + wxMenuBarBase::Detach() ; +} + +void wxMenuBar::Attach(wxFrame *frame) +{ + wxMenuBarBase::Attach( frame ) ; +} + +// --------------------------------------------------------------------------- +// wxMenuBar searching for menu items +// --------------------------------------------------------------------------- + +// Find the itemString in menuString, and return the item id or wxNOT_FOUND +int wxMenuBar::FindMenuItem(const wxString& menuString, + const wxString& itemString) const +{ + wxString menuLabel = wxStripMenuCodes(menuString); + size_t count = GetMenuCount(); + for ( size_t i = 0; i < count; i++ ) + { + wxString title = wxStripMenuCodes(m_titles[i]); + if ( menuLabel == title ) + return _wxMenuAt(m_menus, i)->FindItem(itemString); + } + + return wxNOT_FOUND; +} + +wxMenuItem *wxMenuBar::FindItem(int id, wxMenu **itemMenu) const +{ + if ( itemMenu ) + *itemMenu = NULL; + + wxMenuItem *item = NULL; + size_t count = GetMenuCount(); + for ( size_t i = 0; !item && (i < count); i++ ) + item = _wxMenuAt(m_menus, i)->FindItem(id, itemMenu); + + return item; +} diff --git a/src/osx/carbon/menuitem.cpp b/src/osx/carbon/menuitem.cpp new file mode 100644 index 0000000000..efe921ff74 --- /dev/null +++ b/src/osx/carbon/menuitem.cpp @@ -0,0 +1,289 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/menuitem.cpp +// Purpose: wxMenuItem implementation +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/menuitem.h" +#include "wx/stockitem.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/menu.h" +#endif // WX_PRECOMP + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject) + + +wxMenuItem::wxMenuItem(wxMenu *pParentMenu, + int id, + const wxString& text, + const wxString& strHelp, + wxItemKind kind, + wxMenu *pSubMenu) + :wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu) +{ + wxASSERT_MSG( id != 0 || pSubMenu != NULL , wxT("A MenuItem ID of Zero does not work under Mac") ) ; + + // In other languages there is no difference in naming the Exit/Quit menu item between MacOS and Windows guidelines + // therefore these item must not be translated + if ( wxStripMenuCodes(m_text).Upper() == wxT("EXIT") ) + m_text = wxT("Quit\tCtrl+Q") ; + + m_radioGroup.start = -1; + m_isRadioGroupStart = false; +} + +wxMenuItem::~wxMenuItem() +{ +} + +// change item state +// ----------------- + +void wxMenuItem::SetBitmap(const wxBitmap& bitmap) +{ + m_bitmap = bitmap; + UpdateItemBitmap(); +} + +void wxMenuItem::UpdateItemBitmap() +{ + if ( !m_parentMenu ) + return ; + + MenuHandle mhandle = MAC_WXHMENU(m_parentMenu->GetHMenu()) ; + MenuItemIndex index = m_parentMenu->MacGetIndexFromItem( this ) ; + DoUpdateItemBitmap( mhandle, index ); +} + +void wxMenuItem::DoUpdateItemBitmap( WXHMENU menu, wxUint16 index) +{ + MenuHandle mhandle = (MenuHandle) menu; + + if ( mhandle == NULL || index == 0) + return ; + + if ( m_bitmap.Ok() ) + { +#if wxUSE_BMPBUTTON + ControlButtonContentInfo info ; + wxMacCreateBitmapButton( &info , m_bitmap ) ; + if ( info.contentType != kControlNoContent ) + { + if ( info.contentType == kControlContentIconRef ) + SetMenuItemIconHandle( mhandle , index , + kMenuIconRefType , (Handle) info.u.iconRef ) ; + else if ( info.contentType == kControlContentCGImageRef ) + SetMenuItemIconHandle( mhandle , index , + kMenuCGImageRefType , (Handle) info.u.imageRef ) ; + } + wxMacReleaseBitmapButton( &info ) ; +#endif + } +} + +void wxMenuItem::UpdateItemStatus() +{ + if ( !m_parentMenu ) + return ; + + if ( IsSeparator() ) + return ; + + if ( GetId() == wxApp::s_macPreferencesMenuItemId) + { + if ( !IsEnabled() ) + DisableMenuCommand( NULL , kHICommandPreferences ) ; + else + EnableMenuCommand( NULL , kHICommandPreferences ) ; + } + + if ( GetId() == wxApp::s_macExitMenuItemId) + { + if ( !IsEnabled() ) + DisableMenuCommand( NULL , kHICommandQuit ) ; + else + EnableMenuCommand( NULL , kHICommandQuit ) ; + } + + { + MenuHandle mhandle = MAC_WXHMENU(m_parentMenu->GetHMenu()) ; + MenuItemIndex index = m_parentMenu->MacGetIndexFromItem( this ) ; + if ( mhandle == NULL || index == 0) + return ; + + UMAEnableMenuItem( mhandle , index , m_isEnabled ) ; + if ( IsCheckable() && IsChecked() ) + ::SetItemMark( mhandle , index , 0x12 ) ; // checkmark + else + ::SetItemMark( mhandle , index , 0 ) ; // no mark + + UMASetMenuItemText( mhandle , index , wxStripMenuCodes(m_text) , wxFont::GetDefaultEncoding() ) ; + wxAcceleratorEntry *entry = wxAcceleratorEntry::Create( m_text ) ; + UMASetMenuItemShortcut( mhandle , index , entry ) ; + delete entry ; + } +} + +void wxMenuItem::UpdateItemText() +{ + if ( !m_parentMenu ) + return ; + + MenuHandle mhandle = MAC_WXHMENU(m_parentMenu->GetHMenu()) ; + MenuItemIndex index = m_parentMenu->MacGetIndexFromItem( this ) ; + if (mhandle == NULL || index == 0) + return ; + + wxString text = m_text; + if (text.IsEmpty() && !IsSeparator()) + { + wxASSERT_MSG(wxIsStockID(GetId()), wxT("A non-stock menu item with an empty label?")); + text = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR|wxSTOCK_WITH_MNEMONIC); + } + + UMASetMenuItemText( mhandle , index , wxStripMenuCodes(text) , wxFont::GetDefaultEncoding() ) ; + wxAcceleratorEntry *entry = wxAcceleratorEntry::Create( text ) ; + UMASetMenuItemShortcut( mhandle , index , entry ) ; + delete entry ; +} + +void wxMenuItem::Enable(bool bDoEnable) +{ + if (( m_isEnabled != bDoEnable + // avoid changing menuitem state when menu is disabled + // eg. BeginAppModalStateForWindow() will disable menus and ignore this change + // which in turn causes m_isEnabled to become out of sync with real menuitem state + && !(m_parentMenu && !IsMenuItemEnabled(MAC_WXHMENU(m_parentMenu->GetHMenu()), 0)) ) + // always update builtin menuitems + || ( GetId() == wxApp::s_macPreferencesMenuItemId + || GetId() == wxApp::s_macExitMenuItemId + || GetId() == wxApp::s_macAboutMenuItemId + )) + { + wxMenuItemBase::Enable( bDoEnable ) ; + UpdateItemStatus() ; + } +} + +void wxMenuItem::UncheckRadio() +{ + if ( m_isChecked ) + { + wxMenuItemBase::Check( false ) ; + UpdateItemStatus() ; + } +} + +void wxMenuItem::Check(bool bDoCheck) +{ + wxCHECK_RET( IsCheckable() && !IsSeparator(), wxT("only checkable items may be checked") ); + + if ( m_isChecked != bDoCheck ) + { + if ( GetKind() == wxITEM_RADIO ) + { + if ( bDoCheck ) + { + wxMenuItemBase::Check( bDoCheck ) ; + UpdateItemStatus() ; + + // get the index of this item in the menu + const wxMenuItemList& items = m_parentMenu->GetMenuItems(); + int pos = items.IndexOf(this); + wxCHECK_RET( pos != wxNOT_FOUND, + _T("menuitem not found in the menu items list?") ); + + // get the radio group range + int start, end; + + if ( m_isRadioGroupStart ) + { + // we already have all information we need + start = pos; + end = m_radioGroup.end; + } + else // next radio group item + { + // get the radio group end from the start item + start = m_radioGroup.start; + end = items.Item(start)->GetData()->m_radioGroup.end; + } + + // also uncheck all the other items in this radio group + wxMenuItemList::compatibility_iterator node = items.Item(start); + for ( int n = start; n <= end && node; n++ ) + { + if ( n != pos ) + ((wxMenuItem*)node->GetData())->UncheckRadio(); + + node = node->GetNext(); + } + } + } + else + { + wxMenuItemBase::Check( bDoCheck ) ; + UpdateItemStatus() ; + } + } +} + +void wxMenuItem::SetItemLabel(const wxString& text) +{ + // don't do anything if label didn't change + if ( m_text == text ) + return; + + wxMenuItemBase::SetItemLabel(text); + + UpdateItemText() ; +} + +// radio group stuff +// ----------------- + +void wxMenuItem::SetAsRadioGroupStart() +{ + m_isRadioGroupStart = true; +} + +void wxMenuItem::SetRadioGroupStart(int start) +{ + wxASSERT_MSG( !m_isRadioGroupStart, + wxT("should only be called for the next radio items") ); + + m_radioGroup.start = start; +} + +void wxMenuItem::SetRadioGroupEnd(int end) +{ + wxASSERT_MSG( m_isRadioGroupStart, + wxT("should only be called for the first radio item") ); + + m_radioGroup.end = end; +} + +// ---------------------------------------------------------------------------- +// wxMenuItemBase +// ---------------------------------------------------------------------------- + +wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu, + int id, + const wxString& name, + const wxString& help, + wxItemKind kind, + wxMenu *subMenu) +{ + return new wxMenuItem(parentMenu, id, name, help, kind, subMenu); +} diff --git a/src/osx/carbon/metafile.cpp b/src/osx/carbon/metafile.cpp new file mode 100644 index 0000000000..73d5b77f9a --- /dev/null +++ b/src/osx/carbon/metafile.cpp @@ -0,0 +1,366 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/metafile.cpp +// Purpose: wxMetaFile, wxMetaFileDC etc. These classes are optional. +// Author: Stefan Csomor +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// +// +// Currently, the only purpose for making a metafile +// is to put it on the clipboard. + + +#include "wx/wxprec.h" + +#if wxUSE_METAFILE + +#ifndef WX_PRECOMP + #include "wx/utils.h" + #include "wx/app.h" +#endif + +#include "wx/metafile.h" +#include "wx/clipbrd.h" +#include "wx/mac/uma.h" +#include "wx/graphics.h" +#include "wx/mac/carbon/metafile.h" + +#include +#include + +IMPLEMENT_DYNAMIC_CLASS(wxMetafile, wxObject) +IMPLEMENT_ABSTRACT_CLASS(wxMetafileDC, wxDC) +IMPLEMENT_ABSTRACT_CLASS(wxMetafileDCImpl, wxGCDCImpl) + +#define M_METAFILEREFDATA( a ) ((wxMetafileRefData*)(a).GetRefData()) + +class wxMetafileRefData : public wxGDIRefData +{ +public: + // default ctor needed for CreateGDIRefData(), must be initialized later + wxMetafileRefData() { Init(); } + + // creates a metafile from memory, assumes ownership + wxMetafileRefData(CFDataRef data); + + // prepares a recording metafile + wxMetafileRefData( int width, int height); + + // prepares a metafile to be read from a file (if filename is not empty) + wxMetafileRefData( const wxString& filename); + + virtual ~wxMetafileRefData(); + + virtual bool IsOk() const { return m_data != NULL; } + + void Init(); + + int GetWidth() const { return m_width; } + int GetHeight() const { return m_height; } + + CGPDFDocumentRef GetPDFDocument() const { return m_pdfDoc; } + void UpdateDocumentFromData() ; + + const wxCFDataRef& GetData() const { return m_data; } + CGContextRef GetContext() const { return m_context; } + + // ends the recording + void Close(); +private: + wxCFDataRef m_data; + wxCFRef m_pdfDoc; + CGContextRef m_context; + + int m_width ; + int m_height ; +}; + +wxMetafileRefData::wxMetafileRefData(CFDataRef data) : + m_data(data) +{ + Init(); + UpdateDocumentFromData(); +} + +wxMetafileRefData::wxMetafileRefData( const wxString& filename ) +{ + Init(); + + if ( !filename.empty() ) + { + wxCFRef cfMutableString(CFStringCreateMutableCopy(NULL, 0, wxCFStringRef(filename))); + CFStringNormalize(cfMutableString,kCFStringNormalizationFormD); + wxCFRef url(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfMutableString , kCFURLPOSIXPathStyle, false)); + m_pdfDoc.reset(CGPDFDocumentCreateWithURL(url)); + } +} + + +wxMetafileRefData::wxMetafileRefData( int width, int height) +{ + Init(); + + m_width = width; + m_height = height; + + CGRect r = CGRectMake( 0 , 0 , width , height ); + + CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); + m_data.reset(data); + CGDataConsumerRef dataConsumer = wxMacCGDataConsumerCreateWithCFData(data); + m_context = CGPDFContextCreate( dataConsumer, (width != 0 && height != 0) ? &r : NULL , NULL ); + CGDataConsumerRelease( dataConsumer ); + if ( m_context ) + { + CGPDFContextBeginPage(m_context, NULL); + + CGColorSpaceRef genericColorSpace = wxMacGetGenericRGBColorSpace(); + + CGContextSetFillColorSpace( m_context, genericColorSpace ); + CGContextSetStrokeColorSpace( m_context, genericColorSpace ); + + CGContextTranslateCTM( m_context , 0 , height ) ; + CGContextScaleCTM( m_context , 1 , -1 ) ; + } +} + +wxMetafileRefData::~wxMetafileRefData() +{ +} + +void wxMetafileRefData::Init() +{ + m_context = NULL; + m_width = -1; + m_height = -1; +} + +void wxMetafileRefData::Close() +{ + CGPDFContextEndPage(m_context); + + CGContextRelease(m_context); + m_context = NULL; + + UpdateDocumentFromData(); +} + +void wxMetafileRefData::UpdateDocumentFromData() +{ + wxCFRef provider(wxMacCGDataProviderCreateWithCFData(m_data)); + m_pdfDoc.reset(CGPDFDocumentCreateWithProvider(provider)); + if ( m_pdfDoc != NULL ) + { + CGPDFPageRef page = CGPDFDocumentGetPage( m_pdfDoc, 1 ); + CGRect rect = CGPDFPageGetBoxRect ( page, kCGPDFMediaBox); + m_width = wx_static_cast(int, rect.size.width); + m_height = wx_static_cast(int, rect.size.height); + } +} + +wxMetaFile::wxMetaFile(const wxString& file) +{ + m_refData = new wxMetafileRefData(file); +} + +wxMetaFile::~wxMetaFile() +{ +} + +wxGDIRefData *wxMetaFile::CreateGDIRefData() const +{ + return new wxMetafileRefData; +} + +wxGDIRefData *wxMetaFile::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxMetafileRefData(*wx_static_cast(const wxMetafileRefData *, data)); +} + +WXHMETAFILE wxMetaFile::GetHMETAFILE() const +{ + return (WXHMETAFILE) (CFDataRef) M_METAFILEDATA->GetData(); +} + +bool wxMetaFile::SetClipboard(int WXUNUSED(width), int WXUNUSED(height)) +{ + bool success = true; + +#if wxUSE_DRAG_AND_DROP + if (m_refData == NULL) + return false; + + bool alreadyOpen = wxTheClipboard->IsOpened(); + if (!alreadyOpen) + { + wxTheClipboard->Open(); + wxTheClipboard->Clear(); + } + + wxDataObject *data = new wxMetafileDataObject( *this ); + success = wxTheClipboard->SetData( data ); + if (!alreadyOpen) + wxTheClipboard->Close(); +#endif + + return success; +} + +void wxMetafile::SetHMETAFILE(WXHMETAFILE mf) +{ + UnRef(); + + m_refData = new wxMetafileRefData((CFDataRef)mf); +} + +#ifndef __LP64__ +void wxMetafile::SetPICT(void* pictHandle) +{ + UnRef(); + + Handle picHandle = (Handle) pictHandle; + HLock(picHandle); + CFDataRef data = CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, (const UInt8*) *picHandle, GetHandleSize(picHandle), kCFAllocatorNull); + wxCFRef provider(wxMacCGDataProviderCreateWithCFData(data)); + QDPictRef pictRef = QDPictCreateWithProvider(provider); + CGRect rect = QDPictGetBounds(pictRef); + m_refData = new wxMetafileRefData(wx_static_cast(int, rect.size.width), + wx_static_cast(int, rect.size.height)); + QDPictDrawToCGContext( ((wxMetafileRefData*) m_refData)->GetContext(), rect, pictRef ); + CFRelease( data ); + QDPictRelease( pictRef ); + ((wxMetafileRefData*) m_refData)->Close(); +} +#endif + +bool wxMetaFile::Play(wxDC *dc) +{ + if (!m_refData) + return false; + + if (!dc->IsOk()) + return false; + + { + wxDCImpl *impl = dc->GetImpl(); + wxGCDCImpl *gc_impl = wxDynamicCast(impl, wxGCDCImpl); + if (gc_impl) + { + CGContextRef cg = (CGContextRef) (gc_impl->GetGraphicsContext()->GetNativeContext()); + CGPDFDocumentRef doc = M_METAFILEDATA->GetPDFDocument(); + CGPDFPageRef page = CGPDFDocumentGetPage( doc, 1 ); + wxMacCGContextStateSaver save(cg); + CGContextDrawPDFPage( cg, page ); + } +// CGContextTranslateCTM( cg, 0, bounds.size.width ); +// CGContextScaleCTM( cg, 1, -1 ); + } + + return true; +} + +wxSize wxMetaFile::GetSize() const +{ + wxSize dataSize = wxDefaultSize; + + if (Ok()) + { + dataSize.x = M_METAFILEDATA->GetWidth(); + dataSize.y = M_METAFILEDATA->GetHeight(); + } + + return dataSize; +} + +// Metafile device context + +// New constructor that takes origin and extent. If you use this, don't +// give origin/extent arguments to wxMakeMetaFilePlaceable. + +wxMetafileDCImpl::wxMetafileDCImpl( + wxDC *owner, + const wxString& filename, + int width, int height, + const wxString& WXUNUSED(description) ) : + wxGCDCImpl( owner ) +{ + wxASSERT_MSG( width != 0 || height != 0, wxT("no arbitration of metafile size supported") ); + wxASSERT_MSG( filename.empty(), wxT("no file based metafile support yet")); + + m_metaFile = new wxMetaFile( filename ); + wxMetafileRefData* metafiledata = new wxMetafileRefData(width, height); + m_metaFile->UnRef(); + m_metaFile->SetRefData( metafiledata ); + + SetGraphicsContext( wxGraphicsContext::CreateFromNative(metafiledata->GetContext())); + m_ok = (m_graphicContext != NULL) ; + + SetMapMode( wxMM_TEXT ); +} + +wxMetafileDCImpl::~wxMetafileDCImpl() +{ +} + +void wxMetafileDCImpl::DoGetSize(int *width, int *height) const +{ + wxCHECK_RET( m_metaFile, wxT("GetSize() doesn't work without a metafile") ); + + wxSize sz = m_metaFile->GetSize(); + if (width) + (*width) = sz.x; + if (height) + (*height) = sz.y; +} + +wxMetaFile *wxMetafileDCImpl::Close() +{ + delete m_graphicContext; + m_graphicContext = NULL; + m_ok = false; + + M_METAFILEREFDATA(*m_metaFile)->Close(); + + return m_metaFile; +} + +#if wxUSE_DATAOBJ +size_t wxMetafileDataObject::GetDataSize() const +{ + CFIndex length = 0; + wxMetafileRefData* refData = M_METAFILEREFDATA(m_metafile); + if ( refData ) + length = refData->GetData().GetLength(); + return length; +} + +bool wxMetafileDataObject::GetDataHere(void *buf) const +{ + bool result = false; + + wxMetafileRefData* refData = M_METAFILEREFDATA(m_metafile); + if ( refData ) + { + CFIndex length = refData->GetData().GetLength(); + if ( length > 0 ) + { + result = true ; + refData->GetData().GetBytes(CFRangeMake(0,length), (UInt8 *) buf); + } + } + return result; +} + +bool wxMetafileDataObject::SetData(size_t len, const void *buf) +{ + wxMetafileRefData* metafiledata = new wxMetafileRefData(wxCFRefFromGet(wxCFDataRef((UInt8*)buf, len).get())); + m_metafile.UnRef(); + m_metafile.SetRefData( metafiledata ); + return true; +} +#endif + +#endif diff --git a/src/osx/carbon/mimetmac.cpp b/src/osx/carbon/mimetmac.cpp new file mode 100644 index 0000000000..5f1593ea05 --- /dev/null +++ b/src/osx/carbon/mimetmac.cpp @@ -0,0 +1,1953 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/mimetype.cpp +// Purpose: Mac Carbon implementation for wx MIME-related classes +// Author: Ryan Norton +// Modified by: +// Created: 04/16/2005 +// RCS-ID: $Id$ +// Copyright: (c) 2005 Ryan Norton () +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// +// TODO: Search Info[-macos](classic).plist dictionary in addition +// to Internet Config database. +// +// Maybe try a brainstorm a way to change the wxMimeTypesManager API +// to get info from a file instead/addition to current get all stuff +// API so that we can use Launch Services to get MIME type info. +// +// Implement GetIcon from one of the FinderInfo functions - or +// use Launch Services and search that app's plist for the icon. +// +// Put some special juice in for the print command. +// + +// for compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_MIMETYPE + +#include "wx/mac/mimetype.h" + +#ifndef WX_PRECOMP + #include "wx/dynarray.h" + #include "wx/string.h" + #include "wx/intl.h" + #include "wx/log.h" + + #if wxUSE_GUI + #include "wx/icon.h" + #endif +#endif + +#include "wx/file.h" +#include "wx/confbase.h" + +#include "wx/mac/private.h" + +// other standard headers +#include + +#ifndef __DARWIN__ + #include + #include +#endif + +#ifndef __LP64__ +// START CODE SAMPLE FROM TECHNOTE 1002 (http://developer.apple.com/technotes/tn/tn1002.html) + +// IsRemoteVolume can be used to find out if the +// volume referred to by vRefNum is a remote volume +// located somewhere on a network. the volume's attribute +// flags (copied from the GetVolParmsInfoBuffer structure) +// are returned in the longword pointed to by vMAttrib. +OSErr IsRemoteVolume(short vRefNum, Boolean *isRemote, long *vMAttrib) +{ + HParamBlockRec volPB; + GetVolParmsInfoBuffer volinfo; + OSErr err; + + volPB.ioParam.ioVRefNum = vRefNum; + volPB.ioParam.ioNamePtr = NULL; + volPB.ioParam.ioBuffer = (Ptr)&volinfo; + volPB.ioParam.ioReqCount = sizeof(volinfo); + err = PBHGetVolParmsSync( &volPB ); + if (err == noErr) + { + *isRemote = (volinfo.vMServerAdr != 0); + *vMAttrib = volinfo.vMAttrib; + } + + return err; +} + +// BuildVolumeList fills the array pointed to by vols with +// a list of the currently mounted volumes. If includeRemote +// is true, then remote server volumes will be included in +// the list. When remote server volumes are included in the +// list, they will be added to the end of the list. On entry, +// *count should contain the size of the array pointed to by +// vols. On exit, *count will be set to the number of id numbers +// placed in the array. If vMAttribMask is non-zero, then +// only volumes with matching attributes are added to the +// list of volumes. bits in the vMAttribMask should use the +// same encoding as bits in the vMAttrib field of +// the GetVolParmsInfoBuffer structure. +OSErr BuildVolumeList(Boolean includeRemote, short *vols, + long *count, long vMAttribMask) +{ + HParamBlockRec volPB; + Boolean isRemote; + OSErr err = noErr; + long nlocal, nremote; + long vMAttrib; + + // set up and check parameters + volPB.volumeParam.ioNamePtr = NULL; + nlocal = nremote = 0; + if (*count == 0) + return noErr; + + // iterate through volumes + for (volPB.volumeParam.ioVolIndex = 1; + PBHGetVInfoSync(&volPB) == noErr; + volPB.volumeParam.ioVolIndex++) + { + // skip remote volumes, if necessary + err = IsRemoteVolume(volPB.volumeParam.ioVRefNum, &isRemote, &vMAttrib); + if (err != noErr) + goto bail; + + if ((includeRemote || !isRemote) && ((vMAttrib & vMAttribMask) == vMAttribMask)) + { + // add local volumes at the front; remote volumes at the end + if (isRemote) + vols[nlocal + nremote++] = volPB.volumeParam.ioVRefNum; + else + { + if (nremote > 0) + BlockMoveData( + vols + nlocal, + vols + nlocal + 1, + nremote * sizeof(short) ); + vols[nlocal++] = volPB.volumeParam.ioVRefNum; + } + + // list full? + if ((nlocal + nremote) >= *count) + break; + } + } + +bail: + *count = (nlocal + nremote); + + return err; +} + + +// FindApplication iterates through mounted volumes +// searching for an application with the given creator +// type. If includeRemote is true, then remote volumes +// will be searched (after local ones) for an application +// with the creator type. +// +// Hacked to output to appName +// +#define kMaxVols 20 + +OSErr FindApplication(OSType appCreator, Boolean includeRemote, Str255 appName, FSSpec* appSpec) +{ + short rRefNums[kMaxVols]; + long i, volCount; + DTPBRec desktopPB; + OSErr err; + + // get a list of volumes - with desktop files + volCount = kMaxVols; + err = BuildVolumeList(includeRemote, rRefNums, &volCount, (1 << bHasDesktopMgr) ); + if (err != noErr) + return err; + + // iterate through the list + for (i=0; iname[0] == 0 ) + { + result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec); + } + else + { +#endif + + // Make a copy of the input FSSpec that can be modified + BlockMoveData( spec, &tempSpec, sizeof(FSSpec) ); + + if ( result == noErr ) + { + if ( tempSpec.parID == fsRtParID ) + { + // object is a volume + // Add a colon to make it a full pathname + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + + // We're done + result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); + } + else + { + // object isn't a volume + + // Is the object a file or a directory? + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrDirID = tempSpec.parID; + pb.dirInfo.ioFDirIndex = 0; + result = PBGetCatInfoSync( &pb ); + + // Allow file/directory name at end of path to not exist. + realResult = result; + if ((result == noErr) || (result == fnfErr)) + { + // if the object is a directory, append a colon so full pathname ends with colon + if ((result == noErr) && (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0) + { + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + } + + // Put the object name in first + result = PtrToHand( &tempSpec.name[1], fullPath, tempSpec.name[0] ); + if ( result == noErr ) + { + // Get the ancestor directory names + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrParID = tempSpec.parID; + + // loop until we have an error or find the root directory + do + { + pb.dirInfo.ioFDirIndex = -1; + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + result = PBGetCatInfoSync(&pb); + if ( result == noErr ) + { + // Append colon to directory name + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + + // Add directory name to beginning of fullPath + (void)Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]); + result = MemError(); + } + } + while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) ); + } + } + } + } + + if ( result == noErr ) + { + // Return the length + *fullPathLength = GetHandleSize( *fullPath ); + result = realResult; // return realResult in case it was fnfErr + } + else + { + // Dispose of the handle and return NULL and zero length + if ( *fullPath != NULL ) + { + DisposeHandle( *fullPath ); + *fullPath = NULL; + } + *fullPathLength = 0; + } + + return result; +} +#endif // LP64 +// +// On the mac there are two ways to open a file - one is through apple events and the +// finder, another is through mime types. +// +// So, really there are two ways to implement wxFileType... +// +// Mime types are only available on OS 8.1+ through the InternetConfig API +// +// Much like the old-style file manager, it has 3 levels of flexibility for its methods - +// Low - which means you have to iterate yourself through the mime database +// Medium - which lets you sort of cache the database if you want to use lowlevel functions +// High - which requires access to the database every time +// +// We want to be efficient (i.e. professional :) ) about it, so we use a combo of low +// and mid-level functions +// +// TODO: Should we call ICBegin/ICEnd? Then where? +// + +// debug helper +inline void wxLogMimeDebug(const wxChar* WXUNUSED_UNLESS_DEBUG(szMsg), OSStatus WXUNUSED_UNLESS_DEBUG(status)) +{ + wxLogDebug(wxString::Format(wxT("%s LINE:%i OSERROR:%i"), szMsg, __LINE__, (int)status)); +} + +// in case we're compiling in non-GUI mode +class WXDLLIMPEXP_FWD_CORE wxIcon; + +bool wxFileTypeImpl::SetCommand(const wxString& WXUNUSED(cmd), const wxString& WXUNUSED(verb), bool WXUNUSED(overwriteprompt)) +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + return false; +} + +bool wxFileTypeImpl::SetDefaultIcon(const wxString& WXUNUSED(strIcon), int WXUNUSED(index)) +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + return false; +} + +bool wxFileTypeImpl::GetOpenCommand(wxString *openCmd, + const wxFileType::MessageParameters& params) const +{ + wxString cmd = GetCommand(wxT("open")); + + *openCmd = wxFileType::ExpandCommand(cmd, params); + + return !openCmd->empty(); +} + +bool +wxFileTypeImpl::GetPrintCommand( + wxString *printCmd, + const wxFileType::MessageParameters& params) const +{ + wxString cmd = GetCommand(wxT("print")); + + *printCmd = wxFileType::ExpandCommand(cmd, params); + + return !printCmd->empty(); +} + +// +// Internet Config vs. Launch Services +// +// From OS 8 on there was internet config... +// However, OSX and its finder does not use info +// from Internet Config at all - the Internet Config +// database ONLY CONTAINS APPS THAT ARE CLASSIC APPS +// OR REGISTERED THROUGH INTERNET CONFIG +// +// Therefore on OSX in order for the open command to be useful +// we need to go straight to launch services +// + +//on darwin, use launch services + +#include + +wxString wxFileTypeImpl::GetCommand(const wxString& verb) const +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + if (verb == wxT("open")) + { + ICMapEntry entry; + ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, + m_lIndex, &entry); + + wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); + sCurrentExtension = sCurrentExtension.Right(sCurrentExtension.length()-1 ); + + //type, creator, ext, roles, outapp (FSRef), outappurl + CFURLRef cfurlAppPath; + OSStatus status = LSGetApplicationForInfo( kLSUnknownType, + kLSUnknownCreator, + wxCFStringRef(sCurrentExtension, wxLocale::GetSystemEncoding()), + kLSRolesAll, + NULL, + &cfurlAppPath ); + + if (status == noErr) + { + CFStringRef cfsUnixPath = CFURLCopyFileSystemPath(cfurlAppPath, kCFURLPOSIXPathStyle); + CFRelease(cfurlAppPath); + + // PHEW! Success! + // Since a filename might have spaces in it, so surround it with quotes + if (cfsUnixPath) + { + wxString resultStr; + + resultStr = + wxString(wxT("'")) + + wxCFStringRef(cfsUnixPath).AsString(wxLocale::GetSystemEncoding()) + + wxString(wxT("'")); + + return resultStr; + } + } + else + { + wxLogDebug(wxString::Format(wxT("%i - %s - %i"), + __LINE__, + wxT("LSGetApplicationForInfo failed."), + (int)status)); + } + } + + return wxEmptyString; +} + +bool wxFileTypeImpl::GetDescription(wxString *desc) const +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + ICMapEntry entry; + ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, m_lIndex, &entry ); + + *desc = wxMacMakeStringFromPascal( entry.entryName ); + + return true; +} + +bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions) +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + ICMapEntry entry; + ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, m_lIndex, &entry ); + + //entry has period in it + wxString sCurrentExtension = wxMacMakeStringFromPascal( entry.extension ); + extensions.Add( sCurrentExtension.Right( sCurrentExtension.length() - 1 ) ); + + return true; +} + +bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + ICMapEntry entry; + ICGetMapEntry( (ICInstance) m_manager->m_hIC, + (Handle) m_manager->m_hDatabase, m_lIndex, &entry ); + + *mimeType = wxMacMakeStringFromPascal(entry.MIMEType); + + return true; +} + +bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const +{ + wxString s; + + if (GetMimeType(&s)) + { + mimeTypes.Clear(); + mimeTypes.Add(s); + + return true; + } + + return false; +} + +bool wxFileTypeImpl::GetIcon(wxIconLocation *WXUNUSED(icon)) const +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + // no such file type or no value or incorrect icon entry + return false; +} + +size_t wxFileTypeImpl::GetAllCommands(wxArrayString * verbs, + wxArrayString * commands, + const wxFileType::MessageParameters& params) const +{ + wxASSERT_MSG( m_manager != NULL , wxT("Bad wxFileType") ); + + wxString sCommand; + size_t ulCount = 0; + + if (GetOpenCommand(&sCommand, params)) + { + verbs->Add(wxString(wxT("open"))); + commands->Add(sCommand); + ++ulCount; + } + + return ulCount; +} + +void wxMimeTypesManagerImpl::Initialize(int WXUNUSED(mailcapStyles), const wxString& WXUNUSED(extraDir)) +{ + wxASSERT_MSG(m_hIC == NULL, wxT("Already initialized wxMimeTypesManager!")); + + // some apps (non-wx) use the 'plst' resource instead +#if 0 + CFBundleRef cfbMain = CFBundleGetMainBundle(); + wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN ); + wxString sLog; + cfdInfo.PrintOut(sLog); + wxLogDebug(sLog); +#endif + + // start Internet Config - log if there's an error + // the second param is the signature of the application, also known + // as resource ID 0. However, as per some recent discussions, we may not + // have a signature for this app, so a generic 'APPL' which is the executable + // type will work for now. + OSStatus status = ICStart( (ICInstance*)&m_hIC, 'APPL' ); + + if (status != noErr) + { + wxLogDebug(wxT("Could not initialize wxMimeTypesManager!")); + wxFAIL; + m_hIC = NULL; + + return; + } + + ICAttr attr; + m_hDatabase = (void**) NewHandle(0); + status = ICFindPrefHandle( (ICInstance) m_hIC, kICMapping, &attr, (Handle) m_hDatabase ); + + //the database file can be corrupt (on OSX its + //~/Library/Preferences/com.apple.internetconfig.plist) + //- bail if it is + if (status != noErr) + { + ClearData(); + wxLogDebug(wxT("Corrupt MIME database!")); + return; + } + + //obtain the number of entries in the map + status = ICCountMapEntries( (ICInstance) m_hIC, (Handle) m_hDatabase, &m_lCount ); + wxASSERT( status == noErr ); + +#if 0 + //debug stuff + ICMapEntry entry; + long pos; + + for (long i = 1; i <= m_lCount; ++i) + { + OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); + + if (status == noErr) + { + wxString sCreator = wxMacMakeStringFromPascal(entry.creatorAppName); + wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); + wxString sMIMEType = wxMacMakeStringFromPascal(entry.MIMEType); + + wxFileTypeImpl impl; + impl.Init(this, pos); + + if (sMIMEType == wxT("text/html") && sCurrentExtension == wxT(".html")) + { + wxString cmd; + + impl.GetOpenCommand( &cmd, wxFileType::MessageParameters (wxT("http://www.google.com"))); + wxPrintf(wxT("APP: [%s]\n"), cmd.c_str()); + } + } + } +#endif +} + +void wxMimeTypesManagerImpl::ClearData() +{ + if (m_hIC != NULL) + { + DisposeHandle( (Handle)m_hDatabase ); + + // this can return an error, but we don't really care that much about it + ICStop( (ICInstance)m_hIC ); + m_hIC = NULL; + } +} + +// +// Q) Iterating through the map - why does it use if (err == noErr) instead of just asserting? +// A) Some intermediate indexes are bad while subsequent ones may be good. Its wierd, I know. +// + +// extension -> file type +wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e) +{ + wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); + + //low level functions - iterate through the database + ICMapEntry entry; + long pos; + + for (long i = 1; i <= m_lCount; ++i) + { + OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); + + if (status == noErr) + { + wxString sCurrentExtension = wxMacMakeStringFromPascal(entry.extension); + if ( sCurrentExtension.Right(sCurrentExtension.length() - 1) == e ) // entry has period in it + { + wxFileType* pFileType = new wxFileType(); + pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); + + return pFileType; + } + } + } + + return NULL; +} + +// MIME type -> extension -> file type +wxFileType* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType) +{ + wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); + + ICMapEntry entry; + long pos; + + // low level functions - iterate through the database + for (long i = 1; i <= m_lCount; ++i) + { + OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); + wxASSERT_MSG( status == noErr, wxString::Format(wxT("Error: %d"), (int)status) ); + + if (status == noErr) + { + if ( wxMacMakeStringFromPascal(entry.MIMEType) == mimeType) + { + wxFileType* pFileType = new wxFileType(); + pFileType->m_impl->Init((wxMimeTypesManagerImpl*)this, pos); + + return pFileType; + } + } + } + + return NULL; +} + +size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes) +{ + wxASSERT_MSG( m_hIC != NULL, wxT("wxMimeTypesManager not Initialized!") ); + + ICMapEntry entry; + long pos, lStartCount; + + // low level functions - iterate through the database + lStartCount = (long) mimetypes.GetCount(); + for (long i = 1; i <= m_lCount; ++i) + { + OSStatus status = ICGetIndMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, i, &pos, &entry ); + if ( status == noErr ) + mimetypes.Add( wxMacMakeStringFromPascal(entry.MIMEType) ); + } + + return mimetypes.GetCount() - lStartCount; +} + +pascal OSStatus MoreProcGetProcessTypeSignature( + const ProcessSerialNumberPtr pPSN, + OSType *pProcessType, + OSType *pCreator) +{ + OSStatus anErr = noErr; + ProcessInfoRec infoRec; + ProcessSerialNumber localPSN; + + infoRec.processInfoLength = sizeof(ProcessInfoRec); + infoRec.processName = NULL; +#ifndef __LP64__ + infoRec.processAppSpec = NULL; +#endif + + if ( pPSN == NULL ) + { + localPSN.highLongOfPSN = 0; + localPSN.lowLongOfPSN = kCurrentProcess; + } + else + { + localPSN = *pPSN; + } + + anErr = GetProcessInformation(&localPSN, &infoRec); + if (anErr == noErr) + { + *pProcessType = infoRec.processType; + *pCreator = infoRec.processSignature; + } + + return anErr; +} + +// +// +// TODO: clean this up, its messy +// +// +// + +#include "wx/mac/corefoundation/cfstring.h" + +#define wxCF_RELEASE true +#define wxCF_RETAIN false + +// ---------------------------------------------------------------------------- +// wxCFDictionary +// ---------------------------------------------------------------------------- + +class wxCFDictionary +{ +public: + wxCFDictionary(CFTypeRef ref) + { + m_cfmdRef = (CFMutableDictionaryRef) ref; + } + + wxCFDictionary(CFIndex cfiSize = 0) + { + CFDictionaryKeyCallBacks kcbs; + CFDictionaryValueCallBacks vcbs; + BuildKeyCallbacks(&kcbs); + BuildValueCallbacks(&vcbs); + + m_cfmdRef = CFDictionaryCreateMutable( + kCFAllocatorDefault, cfiSize, &kcbs, &vcbs ); + } + + ~wxCFDictionary() + { Clear(); } + + void Clear() + { + if (m_cfmdRef) + CFRelease(m_cfmdRef); + } + + static const void* RetainProc(CFAllocatorRef, const void* v) + { return (const void*) CFRetain(v); } + + static void ReleaseProc(CFAllocatorRef, const void* v) + { CFRelease(v); } + + void MakeMutable(CFIndex cfiSize = 0) + { + CFDictionaryRef oldref = (CFDictionaryRef) m_cfmdRef; + + m_cfmdRef = CFDictionaryCreateMutableCopy( + kCFAllocatorDefault, cfiSize, oldref ); + + CFRelease( oldref ); + } + + void BuildKeyCallbacks(CFDictionaryKeyCallBacks* pCbs) + { + pCbs->version = 0; + pCbs->retain = RetainProc; + pCbs->release = ReleaseProc; + pCbs->copyDescription = NULL; + pCbs->equal = NULL; + pCbs->hash = NULL; + } + + void BuildValueCallbacks(CFDictionaryValueCallBacks* pCbs) + { + pCbs->version = 0; + pCbs->retain = RetainProc; + pCbs->release = ReleaseProc; + pCbs->copyDescription = NULL; + pCbs->equal = NULL; + } + + operator CFTypeRef () const + { return (CFTypeRef)m_cfmdRef; } + + CFDictionaryRef GetCFDictionary() const + { return (CFDictionaryRef)m_cfmdRef; } + + CFMutableDictionaryRef GetCFMutableDictionary() + { return (CFMutableDictionaryRef) m_cfmdRef; } + + CFTypeRef operator [] (CFTypeRef cftEntry) const + { + wxASSERT(IsValid()); + return (CFTypeRef) CFDictionaryGetValue((CFDictionaryRef)m_cfmdRef, cftEntry); + } + + CFIndex GetCount() const + { + wxASSERT(IsValid()); + return CFDictionaryGetCount((CFDictionaryRef)m_cfmdRef); + } + + void Add(CFTypeRef cftKey, CFTypeRef cftValue) + { + wxASSERT(IsValid()); + wxASSERT(Exists(cftKey) == false); + CFDictionaryAddValue(m_cfmdRef, cftKey, cftValue); + } + + void Remove(CFTypeRef cftKey) + { + wxASSERT(IsValid()); + wxASSERT(Exists(cftKey)); + CFDictionaryRemoveValue(m_cfmdRef, cftKey); + } + + void Set(CFTypeRef cftKey, CFTypeRef cftValue) + { + wxASSERT(IsValid()); + wxASSERT(Exists(cftKey)); + CFDictionarySetValue(m_cfmdRef, cftKey, cftValue); + } + + bool Exists(CFTypeRef cftKey) const + { + wxASSERT(IsValid()); + return CFDictionaryContainsKey((CFDictionaryRef)m_cfmdRef, cftKey); + } + + bool IsOk() const + { return m_cfmdRef != NULL; } + + bool IsValid() const + { return IsOk() && CFGetTypeID((CFTypeRef)m_cfmdRef) == CFDictionaryGetTypeID(); } + + void PrintOut(wxString& sMessage) + { + PrintOutDictionary(sMessage, m_cfmdRef); + } + + static void PrintOutDictionary(wxString& sMessage, CFDictionaryRef cfdRef) + { + CFIndex cfiCount = CFDictionaryGetCount(cfdRef); + CFTypeRef* pKeys = new CFTypeRef[cfiCount]; + CFTypeRef* pValues = new CFTypeRef[cfiCount]; + + CFDictionaryGetKeysAndValues(cfdRef, pKeys, pValues); + + for (CFIndex i = 0; i < cfiCount; ++i) + { + wxString sKey = wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID(pKeys[i]))).AsString(); + wxString sValue = wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID(pValues[i]))).AsString(); + + sMessage << + wxString::Format(wxT("[{#%d} Key : %s]"), (int) i, + sKey.c_str()); + + PrintOutType(sMessage, sKey, pKeys[i]); + + sMessage << + wxString::Format(wxT("\n\t[Value : %s]"), + sValue.c_str()); + + PrintOutType(sMessage, sValue, pValues[i]); + + sMessage << wxT("\n"); + } + + delete [] pKeys; + delete [] pValues; + } + + static void PrintOutArray(wxString& sMessage, CFArrayRef cfaRef) + { + for (CFIndex i = 0; i < CFArrayGetCount(cfaRef); ++i) + { + wxString sValue = wxCFStringRef(CFCopyTypeIDDescription(CFGetTypeID( + CFArrayGetValueAtIndex(cfaRef, i) + ))).AsString(); + + sMessage << + wxString::Format(wxT("\t\t[{#%d} ArrayValue : %s]\n"), (int) i, + sValue.c_str()); + + PrintOutType(sMessage, sValue, CFArrayGetValueAtIndex(cfaRef, i)); + } + } + + static void PrintOutType(wxString& sMessage, const wxString& sValue, CFTypeRef cfRef) + { + sMessage << wxT(" {"); + + if (sValue == wxT("CFString")) + { + sMessage << wxCFStringRef(wxCFRetain((CFStringRef)cfRef)).AsString(); + } + else if (sValue == wxT("CFNumber")) + { + int nOut; + CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType, &nOut); + sMessage << nOut; + } + else if (sValue == wxT("CFDictionary")) + { + PrintOutDictionary(sMessage, (CFDictionaryRef)cfRef); + } + else if (sValue == wxT("CFArray")) + { + PrintOutArray(sMessage, (CFArrayRef)cfRef); + } + else if (sValue == wxT("CFBoolean")) + { + sMessage << (cfRef == kCFBooleanTrue ? wxT("true") : wxT("false")); + } + else if (sValue == wxT("CFURL")) + { + sMessage << wxCFStringRef(CFURLCopyPath((CFURLRef) cfRef)).AsString(); + } + else + { + sMessage << wxT("*****UNKNOWN TYPE******"); + } + + sMessage << wxT("} "); + } + +#if wxUSE_MIMETYPE + void MakeValidXML(); +#endif + + CFTypeRef WriteAsXML() + { + return CFPropertyListCreateXMLData(kCFAllocatorDefault, m_cfmdRef); + } + + bool ReadAsXML(CFTypeRef cfData, wxString* pErrorMsg = NULL) + { + Clear(); + CFStringRef cfsError=NULL; + m_cfmdRef = (CFMutableDictionaryRef) CFPropertyListCreateFromXMLData( + kCFAllocatorDefault, + (CFDataRef)cfData, + kCFPropertyListMutableContainersAndLeaves, + &cfsError ); + if (cfsError) + { + if (pErrorMsg) + *pErrorMsg = wxCFStringRef(cfsError).AsString(); + else + CFRelease(cfsError); + } + + return m_cfmdRef != NULL; + } + +private: + CFMutableDictionaryRef m_cfmdRef; +}; + +// ---------------------------------------------------------------------------- +// wxCFArray +// ---------------------------------------------------------------------------- + +class wxCFArray +{ +public: + wxCFArray(CFTypeRef ref) + { + m_cfmaRef = (CFMutableArrayRef)ref; + } + + wxCFArray(CFIndex cfiSize = 0) : m_cfmaRef(NULL) + { Create(cfiSize); } + + ~wxCFArray() + { Clear(); } + + void MakeMutable(CFIndex cfiSize = 0) + { + wxASSERT(IsValid()); + + CFMutableArrayRef oldref = m_cfmaRef; + m_cfmaRef = CFArrayCreateMutableCopy( + kCFAllocatorDefault, + cfiSize, + (CFArrayRef)oldref); + CFRelease(oldref); + } + + void BuildCallbacks(CFArrayCallBacks* pCbs) + { + pCbs->version = 0; + pCbs->retain = RetainProc; + pCbs->release = ReleaseProc; + pCbs->copyDescription = NULL; + pCbs->equal = NULL; + } + + void Create(CFIndex cfiSize = 0) + { + Clear(); + CFArrayCallBacks cb; + BuildCallbacks(&cb); + + m_cfmaRef = CFArrayCreateMutable(kCFAllocatorDefault, cfiSize, &cb); + } + + void Clear() + { if (m_cfmaRef) CFRelease(m_cfmaRef); } + + static const void* RetainProc(CFAllocatorRef, const void* v) + { return (const void*) CFRetain(v); } + + static void ReleaseProc(CFAllocatorRef, const void* v) + { CFRelease(v); } + + operator CFTypeRef () const + { return (CFTypeRef)m_cfmaRef; } + + CFArrayRef GetCFArray() const + { return (CFArrayRef)m_cfmaRef; } + + CFMutableArrayRef GetCFMutableArray() + { return (CFMutableArrayRef) m_cfmaRef; } + + CFTypeRef operator [] (CFIndex cfiIndex) const + { + wxASSERT(IsValid()); + return (CFTypeRef) CFArrayGetValueAtIndex((CFArrayRef)m_cfmaRef, cfiIndex); + } + + CFIndex GetCount() + { + wxASSERT(IsValid()); + return CFArrayGetCount((CFArrayRef)m_cfmaRef); + } + + void Add(CFTypeRef cftValue) + { + wxASSERT(IsValid()); + CFArrayAppendValue(m_cfmaRef, cftValue); + } + + void Remove(CFIndex cfiIndex) + { + wxASSERT(IsValid()); + wxASSERT(cfiIndex < GetCount()); + CFArrayRemoveValueAtIndex(m_cfmaRef, cfiIndex); + } + + void Set(CFIndex cfiIndex, CFTypeRef cftValue) + { + wxASSERT(IsValid()); + wxASSERT(cfiIndex < GetCount()); + CFArraySetValueAtIndex(m_cfmaRef, cfiIndex, cftValue); + } + + bool IsOk() const + { return m_cfmaRef != NULL; } + + bool IsValid() const + { + return IsOk() && CFGetTypeID((CFTypeRef)m_cfmaRef) == CFArrayGetTypeID(); + } + +#if wxUSE_MIMETYPE + void MakeValidXML(); +#endif + +private: + CFMutableArrayRef m_cfmaRef; +}; + +// ---------------------------------------------------------------------------- +// wxCFNumber +// ---------------------------------------------------------------------------- + +class wxCFNumber +{ +public: + wxCFNumber(int nValue) + { + m_cfnRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &nValue); + } + + wxCFNumber(CFTypeRef ref) : m_cfnRef((CFNumberRef)ref) + { + } + + virtual ~wxCFNumber() + { + if (m_cfnRef) + CFRelease(m_cfnRef); + } + + operator CFTypeRef() const + { return (CFTypeRef) m_cfnRef; } + + int GetValue() + { + int nOut; + CFNumberGetValue( m_cfnRef, kCFNumberIntType, &nOut ); + + return nOut; + } + + bool IsOk() + { return m_cfnRef != NULL; } + +private: + CFNumberRef m_cfnRef; +}; + +// ---------------------------------------------------------------------------- +// wxCFURL +// ---------------------------------------------------------------------------- + +class wxCFURL +{ +public: + wxCFURL(CFTypeRef ref = NULL) : m_cfurlRef((CFURLRef)ref) + { + } + + wxCFURL(const wxCFStringRef& URLString, CFTypeRef BaseURL = NULL) + { + Create(URLString, BaseURL); + } + + void Create(const wxCFStringRef& URLString, CFTypeRef BaseURL = NULL) + { + m_cfurlRef = CFURLCreateWithString( + kCFAllocatorDefault, + (CFStringRef)(CFTypeRef)URLString, + (CFURLRef) BaseURL); + } + + virtual ~wxCFURL() + { + if (m_cfurlRef) + CFRelease(m_cfurlRef); + } + + wxString BuildWXString() + { + return wxCFStringRef(CFURLCopyPath(m_cfurlRef)).AsString(); + } + + operator CFTypeRef() const + { return (CFTypeRef)m_cfurlRef; } + + bool IsOk() + { return m_cfurlRef != NULL; } + +private: + CFURLRef m_cfurlRef; +}; + +// ---------------------------------------------------------------------------- +// wxCFData +// ---------------------------------------------------------------------------- + +#define wxCFDATA_RELEASEBUFFER 1 + +class wxCFData +{ +public: + wxCFData(CFTypeRef ref) : m_cfdaRef((CFDataRef)ref) + { + } + + wxCFData(const UInt8* pBytes, CFIndex len, bool bKeep = wxCFDATA_RELEASEBUFFER) + { + if (bKeep == wxCFDATA_RELEASEBUFFER) + { + m_cfdaRef = CFDataCreateWithBytesNoCopy + (kCFAllocatorDefault, pBytes, len, kCFAllocatorDefault); + } + else + { + m_cfdaRef = CFDataCreate(kCFAllocatorDefault, pBytes, len); + } + } + + virtual ~wxCFData() + { + if (m_cfdaRef) + CFRelease(m_cfdaRef); + } + + const UInt8* GetValue() + { return CFDataGetBytePtr(m_cfdaRef); } + + CFIndex GetCount() + { return CFDataGetLength(m_cfdaRef); } + + operator CFTypeRef() const + { return (CFTypeRef)m_cfdaRef; } + + bool IsOk() + { return m_cfdaRef != NULL; } + +private: + CFDataRef m_cfdaRef; +}; + +void wxCFDictionary::MakeValidXML() +{ + CFIndex cfiCount = GetCount(); + CFTypeRef* pKeys = new CFTypeRef[cfiCount]; + CFTypeRef* pValues = new CFTypeRef[cfiCount]; + + CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues); + + // for plist xml format, all dictionary keys must be cfstrings and + // no values in the dictionary or subkeys/values can be NULL; + // additionally, CFURLs are not allowed + for (CFIndex i = 0; i < cfiCount; ++i) + { + // must be an array, dictionary, string, bool, or int and cannot be null + // and dictionaries can only contain cfstring keys + CFTypeRef cfRef = pValues[i]; + if (!pKeys[i] || + CFGetTypeID(pKeys[i]) != CFStringGetTypeID() || + !cfRef) + { + Remove(pKeys[i]); + --i; + --cfiCount; + delete [] pKeys; + delete [] pValues; + pKeys = new CFTypeRef[cfiCount]; + pValues = new CFTypeRef[cfiCount]; + CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues); + } + else if (CFGetTypeID(cfRef) == CFArrayGetTypeID()) + { + CFRetain(cfRef); + wxCFArray cfaCurrent(cfRef); + cfaCurrent.MakeMutable(); + cfaCurrent.MakeValidXML(); + Set(pKeys[i], cfaCurrent); + } + else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID()) + { + CFRetain(cfRef); + wxCFDictionary cfdCurrent(cfRef); + cfdCurrent.MakeMutable(); + cfdCurrent.MakeValidXML(); + Set(pKeys[i], cfdCurrent); + } + else if ( CFGetTypeID(cfRef) != CFStringGetTypeID() && + CFGetTypeID(cfRef) != CFNumberGetTypeID() && + CFGetTypeID(cfRef) != CFBooleanGetTypeID() ) + { + Remove(pKeys[i]); + --i; + --cfiCount; + delete [] pKeys; + delete [] pValues; + pKeys = new CFTypeRef[cfiCount]; + pValues = new CFTypeRef[cfiCount]; + CFDictionaryGetKeysAndValues(m_cfmdRef, pKeys, pValues); + } + } + + delete [] pValues; + delete [] pKeys; +} + +void wxCFArray::MakeValidXML() +{ + for (CFIndex i = 0; i < GetCount(); ++i) + { + //must be an array, dictionary, string, bool, or int and cannot be null + //and dictionaries can only contain cfstring keys + CFTypeRef cfRef = (*this)[i]; + if (!cfRef) + { + Remove(i); + --i; + } + else if (CFGetTypeID(cfRef) == CFArrayGetTypeID()) + { + CFRetain(cfRef); + wxCFArray cfaCurrent(cfRef); + cfaCurrent.MakeMutable(); + cfaCurrent.MakeValidXML(); + Set(i, cfaCurrent); + } + else if (CFGetTypeID(cfRef) == CFDictionaryGetTypeID()) + { + CFRetain(cfRef); + wxCFDictionary cfdCurrent(cfRef); + cfdCurrent.MakeMutable(); + cfdCurrent.MakeValidXML(); + Set(i, cfdCurrent); + } + else if ( CFGetTypeID(cfRef) != CFStringGetTypeID() && + CFGetTypeID(cfRef) != CFNumberGetTypeID() && + CFGetTypeID(cfRef) != CFBooleanGetTypeID() ) + { + Remove(i); + --i; + } + } +} + +// +// +// +// END TODO +// +// +// + +wxFileType* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo) +{ + bool bInfoSuccess = false; + + const wxArrayString& asExtensions = ftInfo.GetExtensions(); + size_t dwFoundIndex = 0; + if (!asExtensions.GetCount()) + { + wxLogDebug(wxT("Must have extension to associate with")); + } + + // Find and write to Info.plist in main bundle (note that some other + // apps have theirs named differently, i.e. IE's is named Info-macos.plist + // some apps (non-wx) use the 'plst' resource instead + CFBundleRef cfbMain = CFBundleGetMainBundle(); + if (cfbMain) + { + UInt32 dwBundleType, dwBundleCreator; + CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator); + + // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too), + // which will give us the incorrect info.plist path + // otherwise it will be 'APPL', or in the case of a framework, 'FMWK' + if (dwBundleType == 'APPL') + { + wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain)); +// wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain)); + wxString sInfoPath; +// sInfoPath << wxT("file://"); + sInfoPath << cfurlBundleLoc.BuildWXString(); + sInfoPath << wxT("Contents/Info.plist"); + +// wxCFDictionary cfdInfo( CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN ); + wxCFDictionary cfdInfo; + bool bInfoOpenSuccess = false; + wxFile indictfile; + if (indictfile.Open(sInfoPath, wxFile::read)) + { + CFIndex cfiBufLen = (CFIndex) indictfile.Length(); + const UInt8* pBuffer = new UInt8[cfiBufLen]; + indictfile.Read((void*)pBuffer, cfiBufLen); + wxCFData cfdaInDict(pBuffer, cfiBufLen); + wxString sError; + bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError); + if (!bInfoOpenSuccess) + wxLogDebug(sError); + indictfile.Close(); + } + + if (bInfoOpenSuccess) + { + cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 ); + + wxCFArray cfaDocTypes( wxCFRetain( cfdInfo[ wxCFStringRef(wxT("CFBundleDocumentTypes")) ] ) ); + + bool bAddDocTypesArrayToDictionary = !cfaDocTypes.IsOk(); + if (bAddDocTypesArrayToDictionary) + cfaDocTypes.Create(); + else + cfaDocTypes.MakeMutable( cfaDocTypes.GetCount() + 1 ); + + bool bEntryFound = false; + + // search for duplicates + CFIndex i; + for (i = 0; i < cfaDocTypes.GetCount(); ++i) + { + wxCFDictionary cfdDocTypeEntry( wxCFRetain( cfaDocTypes[i] ) ); + + // A lot of apps don't support MIME types for some reason + // so we go by extensions only + wxCFArray cfaExtensions( wxCFRetain( cfdDocTypeEntry[ wxCFStringRef(wxT("CFBundleTypeExtensions")) ] ) ); + + if (!cfaExtensions.IsOk()) + continue; + + for (CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt) + { + for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt) + { + if (asExtensions[iWXExt] == + wxCFStringRef( wxCFRetain( (CFStringRef) cfaExtensions[iExt] ) ).AsString()) + { + bEntryFound = true; + dwFoundIndex = iWXExt; + + break; + } + } //end of wxstring array + + if (bEntryFound) + break; + } //end for cf array + + if (bEntryFound) + break; + } //end for doctypes + + wxCFDictionary cfdNewEntry; + + if (!ftInfo.GetDescription().empty()) + { + cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeName")), + wxCFStringRef(ftInfo.GetDescription()) ); + } + + if (!ftInfo.GetIconFile().empty()) + { + cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeIconFile")), + wxCFStringRef(ftInfo.GetIconFile()) ); + } + + wxCFArray cfaOSTypes; + wxCFArray cfaExtensions; + wxCFArray cfaMimeTypes; + + //OSTypes is a cfarray of four-char-codes - '****' for unrestricted + cfaOSTypes.Add( wxCFStringRef(wxT("****")) ); + cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeOSTypes")), cfaOSTypes ); + + //'*' for unrestricted + if (ftInfo.GetExtensionsCount() != 0) + { + for (size_t iExtension = 0; iExtension < ftInfo.GetExtensionsCount(); ++iExtension) + { + cfaExtensions.Add( wxCFStringRef( asExtensions[iExtension] ) ); + } + + cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeExtensions")), cfaExtensions ); + } + + if (!ftInfo.GetMimeType().empty()) + { + cfaMimeTypes.Add( wxCFStringRef(ftInfo.GetMimeType()) ); + cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeMIMETypes")), cfaMimeTypes ); + } + + // Editor - can perform all actions + // Viewer - all actions except manipulation/saving + // None - can perform no actions + cfdNewEntry.Add( wxCFStringRef(wxT("CFBundleTypeRole")), wxCFStringRef(wxT("Editor")) ); + + // Is application bundled? + cfdNewEntry.Add( wxCFStringRef(wxT("LSTypeIsPackage")), kCFBooleanTrue ); + + if (bEntryFound) + cfaDocTypes.Set(i, cfdNewEntry); + else + cfaDocTypes.Add(cfdNewEntry); + + // set the doc types array in the muted dictionary + if (bAddDocTypesArrayToDictionary) + cfdInfo.Add(wxCFStringRef(wxT("CFBundleDocumentTypes")), cfaDocTypes); + else + cfdInfo.Set(wxCFStringRef(wxT("CFBundleDocumentTypes")), cfaDocTypes); + + cfdInfo.MakeValidXML(); + + wxFile outdictfile; + if (outdictfile.Open(sInfoPath, wxFile::write)) + { + wxCFData cfdaInfo(cfdInfo.WriteAsXML()); + if (cfdaInfo.IsOk()) + { + if (outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) != + (wxFileOffset)cfdaInfo.GetCount()) + { + wxLogDebug(wxT("error in writing to file")); + } + else + { + bInfoSuccess = true; + +//#if defined(__DARWIN__) +// //force launch services to update its database for the finder +// OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true); +// if (status != noErr) +// { +// wxLogDebug(wxT("LSRegisterURL Failed.")); +// } +//#endif + } + outdictfile.Close(); + } + else + { + outdictfile.Close(); + wxLogDebug(wxT("Could not read in new dictionary")); + } + } + else + { + wxLogDebug(wxString(wxT("Could not open [")) + + sInfoPath + wxT("] for writing.")); + } + } + else + { + wxLogDebug(wxT("No info dictionary in main bundle")); + } + } + else + { + wxLogDebug(wxT("Can only call associate from bundled app within XXX.app")); + } + } + else + { + wxLogDebug(wxT("No main bundle")); + } + + if (!bInfoSuccess) + return NULL; + + // on mac you have to embed it into the mac's file reference resource ('FREF' I believe) + // or, alternately, you could just add an entry to m_hDatabase, but you'd need to get + // the app's signature somehow... + + OSType processType, creator; + OSStatus status = MoreProcGetProcessTypeSignature(NULL, &processType, &creator); + + if (status == noErr) + { + Str255 psCreatorName; +#ifndef __LP64__ + FSSpec dummySpec; + status = FindApplication(creator, false, psCreatorName, &dummySpec); +#else + FSRef fsref; + status = LSFindApplicationForInfo( creator, NULL, NULL, &fsref ,NULL); + HFSUniStr255 name; + status = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, &name, NULL, NULL); + CFStringRef str = FSCreateStringFromHFSUniStr( 0 , &name ); + CFStringGetPascalString(str, psCreatorName, 256, CFStringGetSystemEncoding()); + CFRelease( str ); +#endif + + if (status == noErr) + { + //get the file type if it exists - + //if it really does then modify the database then save it, + //otherwise we need to create a whole new entry + wxFileType* pFileType = GetFileTypeFromExtension(asExtensions[dwFoundIndex]); + if (pFileType) + { + ICMapEntry entry; + ICGetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, + pFileType->m_impl->m_lIndex, &entry ); + + memcpy(entry.creatorAppName, psCreatorName, sizeof(Str255)); + entry.fileCreator = creator; + + status = ICSetMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, + pFileType->m_impl->m_lIndex, &entry ); + + //success + if (status == noErr) + { + return pFileType; + + //kICAttrNoChange means we don't care about attributes such as + //locking in the database +// status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, +// kICAttrNoChange, (Handle) m_hDatabase); +// if (status == noErr) +// return pFileType; +// else +// { +// wxLogDebug(wxString::Format(wxT("%i - %s"), (int)status, wxT("ICSetPrefHandle failed."))); +// } + } + else + { + wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetMapEntry failed."))); + } + + // failure - cleanup + delete pFileType; + } + else + { + // TODO: Maybe force all 3 of these to be non-empty? + Str255 psExtension, psMimeType, psDescription; + + wxMacStringToPascal(wxString(wxT(".")) + ftInfo.GetExtensions()[0], psExtension); + wxMacStringToPascal(ftInfo.GetMimeType(), psMimeType); + wxMacStringToPascal(ftInfo.GetDescription(), psDescription); + + Str255 psPostCreatorName; + wxMacStringToPascal(wxEmptyString, psPostCreatorName); + + //add the entry to the database + ICMapEntry entry; + entry.totalLength = sizeof(ICMapEntry); + entry.fixedLength = kICMapFixedLength; + entry.version = 0; + entry.fileType = 0; //TODO: File type? + entry.fileCreator = creator; + entry.postCreator = 0; + entry.flags = kICMapDataForkBit; //TODO: Maybe resource is valid by default too? + PLstrcpy( entry.extension , psExtension ) ; + memcpy(entry.creatorAppName, psCreatorName, sizeof(Str255)); + memcpy(entry.postAppName, psPostCreatorName, sizeof(Str255)); + memcpy(entry.MIMEType, psMimeType, sizeof(Str255)); + memcpy(entry.entryName, psDescription, sizeof(Str255)); + + status = ICAddMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, &entry); + if (status == noErr) + { + return GetFileTypeFromExtension(ftInfo.GetMimeType()); + +// kICAttrNoChange means we don't care about attributes such as +// locking in the database +// status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, +// kICAttrNoChange, (Handle) m_hDatabase); + + // return the entry in the database if successful +// if (status == noErr) +// return GetFileTypeFromExtension(ftInfo.GetMimeType()); +// else +// { +// wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed."))); + // } + } + else + { + wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICAppMapEntry failed."))); + } + } + } // end if FindApplcation was successful + else + { + wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("FindApplication failed."))); + } + } // end if it could obtain app's signature + else + { + wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("GetProcessSignature failed."))); + } + + return NULL; +} + +bool +wxMimeTypesManagerImpl::Unassociate(wxFileType *pFileType) +{ + wxASSERT(pFileType); + bool bInfoSuccess = false; + + wxArrayString asExtensions; + pFileType->GetExtensions(asExtensions); + + if (!asExtensions.GetCount()) + { + wxLogDebug(wxT("Must have extension to disassociate")); + return false; + } + + // Find and write to Info.plist in main bundle (note that some other + // apps have theirs named differently, i.e. IE's is named Info-macos.plist + // some apps (non-wx) use the 'plst' resource instead + CFBundleRef cfbMain = CFBundleGetMainBundle(); + if (cfbMain) + { + UInt32 dwBundleType, dwBundleCreator; + CFBundleGetPackageInfo(cfbMain, &dwBundleType, &dwBundleCreator); + + // if launching terminal non-app, version will be 'BNDL' (generic bundle, maybe in other cases too), + // which will give us the incorrect info.plist path + // otherwise it will be 'APPL', or in the case of a framework, 'FMWK' + if (dwBundleType == 'APPL') + { + + wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyBundleURL(cfbMain)); +// wxCFURL cfurlBundleLoc((CFTypeRef)CFBundleCopyExecutableURL(cfbMain)); + wxString sInfoPath; +// sInfoPath << wxT("file://"); + sInfoPath << cfurlBundleLoc.BuildWXString(); + sInfoPath << wxT("Contents/Info.plist"); + +// wxCFDictionary cfdInfo( (CFTypeRef) CFBundleGetInfoDictionary(cfbMain), wxCF_RETAIN ); + wxCFDictionary cfdInfo; + bool bInfoOpenSuccess = false; + wxFile indictfile; + if (indictfile.Open(sInfoPath, wxFile::read)) + { + CFIndex cfiBufLen = (CFIndex) indictfile.Length(); + const UInt8* pBuffer = new UInt8[cfiBufLen]; + indictfile.Read((void*)pBuffer, cfiBufLen); + wxCFData cfdaInDict(pBuffer, cfiBufLen); + wxString sError; + bInfoOpenSuccess = cfdInfo.ReadAsXML(cfdaInDict, &sError); + if (!bInfoOpenSuccess) + wxLogDebug(sError); + indictfile.Close(); + } + + if (bInfoOpenSuccess) + { + cfdInfo.MakeMutable( cfdInfo.GetCount() + 1 ); + + wxCFArray cfaDocTypes( wxCFRetain( cfdInfo[ wxCFStringRef(wxT("CFBundleDocumentTypes")) ] ) ); + + if (cfaDocTypes.IsOk()) + { + bool bEntryFound = false; + + //search for duplicate + CFIndex i; + for (i = 0; i < cfaDocTypes.GetCount(); ++i) + { + wxCFDictionary cfdDocTypeEntry( wxCFRetain( cfaDocTypes[i] ) ); + + //A lot of apps dont do to mime types for some reason + //so we go by extensions only + wxCFArray cfaExtensions( wxCFRetain( cfdDocTypeEntry[ wxCFStringRef(wxT("CFBundleTypeExtensions")) ]) ); + + if (!cfaExtensions.IsOk()) + continue; + + for (CFIndex iExt = 0; iExt < cfaExtensions.GetCount(); ++iExt) + { + for (size_t iWXExt = 0; iWXExt < asExtensions.GetCount(); ++iWXExt) + { + if (asExtensions[iWXExt] == + wxCFStringRef( wxCFRetain( (CFStringRef) cfaExtensions[iExt] ) ).AsString()) + { + bEntryFound = true; + cfaDocTypes.Remove(i); + cfdInfo.Set( wxCFStringRef(wxT("CFBundleDocumentTypes")) , cfaDocTypes ); + break; + } + } //end of wxstring array + + if (bEntryFound) + break; + } //end for cf array + + if (bEntryFound) + break; + }//end for doctypes + + if (bEntryFound) + { + cfdInfo.MakeValidXML(); + + wxFile outdictfile; + if (outdictfile.Open(sInfoPath, wxFile::write)) + { + wxCFData cfdaInfo(cfdInfo.WriteAsXML()); + if (cfdaInfo.IsOk()) + { + if (outdictfile.Write(cfdaInfo.GetValue(), cfdaInfo.GetCount()) != + (wxFileOffset)cfdaInfo.GetCount()) + { + wxLogDebug(wxT("error in writing to file")); + } + else + { + bInfoSuccess = true; + +//#if defined(__DARWIN__) +// //force launch services to update its database for the finder +// OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true); +// if (status != noErr) +// { +// wxLogDebug(wxT("LSRegisterURL Failed.")); +// } +//#endif + } + outdictfile.Close(); + } + else + { + outdictfile.Close(); + wxLogDebug(wxT("Could not read in new dictionary")); + } + } + else + { + wxLogDebug( + wxString(wxT("Could not open [")) + + sInfoPath + wxT("] for writing.")); + } + } + else + { + wxLogDebug(wxT("Entry not found to remove")); + + wxString sPrintOut; + wxCFDictionary::PrintOutArray(sPrintOut, (CFArrayRef)(CFTypeRef)cfaDocTypes); + wxLogDebug(sPrintOut); + + for (size_t i = 0; i < asExtensions.GetCount(); ++i) + wxLogDebug(asExtensions[i]); + } + } + else + { + wxLogDebug(wxT("No doc types array found")); + wxString sPrintOut; cfdInfo.PrintOut(sPrintOut); wxLogDebug(sPrintOut); + } + } + else + { + wxLogDebug(wxT("No info dictionary in main bundle")); + } + } + else + { + wxLogDebug(wxT("Can only call associate from bundled app within XXX.app")); + } + } + else + { + wxLogDebug(wxT("No main bundle")); + } + + if (!bInfoSuccess) + return false; + + // this should be as easy as removing the entry from the database + // and then saving the database + OSStatus status = ICDeleteMapEntry( (ICInstance) m_hIC, (Handle) m_hDatabase, + pFileType->m_impl->m_lIndex); + + if (status == noErr) + { + return true; + + //kICAttrNoChange means we don't care about attributes such as + //locking in the database +// status = ICSetPrefHandle((ICInstance) m_hIC, kICMapping, +// kICAttrNoChange, (Handle) m_hDatabase); + +// if (status == noErr) +// { +// return true; +// } +// else +// { +// wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICSetPrefHandle failed."))); +// } + } + else + { + wxLogDebug(wxString::Format(wxT("%i - %s"), __LINE__, wxT("ICDeleteMapEntry failed."))); + } + + return false; +} + +#if 0 + CFWriteStreamRef cfwsInfo = CFWriteStreamCreateWithFile( + kCFAllocatorDefault, + (CFURLRef) (CFTypeRef)cfurlInfoLoc ); + +// CFShow(cfdInfo); + if (cfwsInfo) + { + Boolean bOpened = CFWriteStreamOpen(cfwsInfo); + if (bOpened) + { + CFStringRef cfsError; + CFIndex cfiWritten = CFPropertyListWriteToStream((CFPropertyListRef)(CFTypeRef)cfdInfo, + cfwsInfo, + kCFPropertyListXMLFormat_v1_0, //100 + &cfsError); + if (cfsError && cfiWritten == 0) + { + wxLogDebug(wxCFStringRef(cfsError).BuildWXString()); + wxString sMessage; + cfdInfo.PrintOut(sMessage); + wxLogDebug(sMessage); + } + else + { + bInfoSuccess = true; +//#if defined(__DARWIN__) +// //force launch services to update its database for the finder +// OSStatus status = LSRegisterURL((CFURLRef)(CFTypeRef)cfurlBundleLoc, true); +// if (status != noErr) +// { +// wxLogDebug(wxT("LSRegisterURL Failed.")); +// } +//#endif + } + + CFWriteStreamClose(cfwsInfo); +#endif + +#endif //wxUSE_MIMETYPE diff --git a/src/osx/carbon/minifram.cpp b/src/osx/carbon/minifram.cpp new file mode 100644 index 0000000000..f7266d529e --- /dev/null +++ b/src/osx/carbon/minifram.cpp @@ -0,0 +1,20 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: minifram.cpp +// Purpose: wxMiniFrame. Optional; identical to wxFrame if not supported. +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_MINIFRAME + +#include "wx/minifram.h" + +IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame, wxFrame) + +#endif // wxUSE_MINIFRAME diff --git a/src/osx/carbon/morefile/DirectoryCopy.c b/src/osx/carbon/morefile/DirectoryCopy.c new file mode 100644 index 0000000000..dd484c9b2c --- /dev/null +++ b/src/osx/carbon/morefile/DirectoryCopy.c @@ -0,0 +1,685 @@ +/* + File: DirectoryCopy.c + + Contains: A robust, general purpose directory copy routine. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFiles.h" +#include "MoreFilesExtras.h" +#include "MoreDesktopMgr.h" +#include "FileCopy.h" +#include "DirectoryCopy.h" + +/*****************************************************************************/ + +/* local constants */ + +enum +{ + dirCopyBigCopyBuffSize = 0x00004000, + dirCopyMinCopyBuffSize = 0x00000200 +}; + + +/*****************************************************************************/ + +/* local data structures */ + +/* The EnumerateGlobals structure is used to minimize the amount of +** stack space used when recursively calling CopyLevel and to hold +** global information that might be needed at any time. */ + +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#endif +struct EnumerateGlobals +{ + Ptr copyBuffer; /* pointer to buffer used for file copy operations */ + long bufferSize; /* the size of the copy buffer */ + CopyErrProcPtr errorHandler; /* pointer to error handling function */ + CopyFilterProcPtr copyFilterProc; /* pointer to filter function */ + OSErr error; /* temporary holder of results - saves 2 bytes of stack each level */ + Boolean bailout; /* set to true to by error handling function if fatal error */ + short destinationVRefNum; /* the destination vRefNum */ + Str63 itemName; /* the name of the current item */ + CInfoPBRec myCPB; /* the parameter block used for PBGetCatInfo calls */ +}; +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#endif + +typedef struct EnumerateGlobals EnumerateGlobals; +typedef EnumerateGlobals *EnumerateGlobalsPtr; + + +/* The PreflightGlobals structure is used to minimize the amount of +** stack space used when recursively calling GetLevelSize and to hold +** global information that might be needed at any time. */ + +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#endif +struct PreflightGlobals +{ + OSErr result; /* temporary holder of results - saves 2 bytes of stack each level */ + Str63 itemName; /* the name of the current item */ + CInfoPBRec myCPB; /* the parameter block used for PBGetCatInfo calls */ + + unsigned long dstBlksPerAllocBlk; /* the number of 512 byte blocks per allocation block on destination */ + + unsigned long allocBlksNeeded; /* the total number of allocation blocks needed */ + + unsigned long tempBlocks; /* temporary storage for calculations (save some stack space) */ + CopyFilterProcPtr copyFilterProc; /* pointer to filter function */ +}; +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#endif + +typedef struct PreflightGlobals PreflightGlobals; +typedef PreflightGlobals *PreflightGlobalsPtr; + +/*****************************************************************************/ + +/* static prototypes */ + +static void GetLevelSize(long currentDirID, + PreflightGlobals *theGlobals); + +static OSErr PreflightDirectoryCopySpace(short srcVRefNum, + long srcDirID, + short dstVRefNum, + CopyFilterProcPtr copyFilterProc, + Boolean *spaceOK); + +static void CopyLevel(long sourceDirID, + long dstDirID, + EnumerateGlobals *theGlobals); + +/*****************************************************************************/ + +static void GetLevelSize(long currentDirID, + PreflightGlobals *theGlobals) +{ + short index = 1; + + do + { + theGlobals->myCPB.dirInfo.ioFDirIndex = index; + theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID; /* we need to do this every time */ + /* through, since GetCatInfo */ + /* returns ioFlNum in this field */ + theGlobals->result = PBGetCatInfoSync(&theGlobals->myCPB); + if ( theGlobals->result == noErr ) + { + if ( (theGlobals->copyFilterProc == NULL) || + CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */ + { + /* Either there's no filter proc OR the filter proc says to use this item */ + if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* we have a directory */ + + GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */ + theGlobals->result = noErr; /* clear error return on way back */ + } + else + { + /* We have a file - add its allocation blocks to allocBlksNeeded. */ + /* Since space on Mac OS disks is always allocated in allocation blocks, */ + /* this takes into account rounding up to the end of an allocation block. */ + + /* get number of 512-byte blocks needed for data fork */ + if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 ) + { + theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1; + } + else + { + theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9; + } + /* now, calculate number of new allocation blocks needed for the data fork and add it to the total */ + if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk ) + { + theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1; + } + else + { + theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk; + } + + /* get number of 512-byte blocks needed for resource fork */ + if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 ) + { + theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1; + } + else + { + theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9; + } + /* now, calculate number of new allocation blocks needed for the resource fork and add it to the total */ + if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk ) + { + theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1; + } + else + { + theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk; + } + } + } + } + ++index; + } while ( theGlobals->result == noErr ); +} + +/*****************************************************************************/ + +static OSErr PreflightDirectoryCopySpace(short srcVRefNum, + long srcDirID, + short dstVRefNum, + CopyFilterProcPtr copyFilterProc, + Boolean *spaceOK) +{ + XVolumeParam pb; + OSErr error; + unsigned long dstFreeBlocks; + PreflightGlobals theGlobals; + + error = XGetVolumeInfoNoName(NULL, dstVRefNum, &pb); + if ( error == noErr ) + { + /* Convert freeBytes to free disk blocks (512-byte blocks) */ + dstFreeBlocks = U32SetU(U64ShiftRight(pb.ioVFreeBytes, 9)); + + /* get allocation block size (always multiple of 512) and divide by 512 + to get number of 512-byte blocks per allocation block */ + theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.ioVAlBlkSiz >> 9); + + theGlobals.allocBlksNeeded = 0; + + theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName; + theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum; + + theGlobals.copyFilterProc = copyFilterProc; + + GetLevelSize(srcDirID, &theGlobals); + + /* Is there enough room on the destination volume for the source file? */ + /* Note: This will work because the largest number of disk blocks supported */ + /* on a 2TB volume is 0xffffffff and (allocBlksNeeded * dstBlksPerAllocBlk) */ + /* will always be less than 0xffffffff. */ + *spaceOK = ((theGlobals.allocBlksNeeded * theGlobals.dstBlksPerAllocBlk) <= dstFreeBlocks); + } + + return ( error ); +} + +/*****************************************************************************/ + +static void CopyLevel(long sourceDirID, + long dstDirID, + EnumerateGlobals *theGlobals) +{ + long currentSrcDirID = 0 ; + long newDirID; + short index = 1; + + do + { + /* Get next source item at the current directory level */ + + theGlobals->myCPB.dirInfo.ioFDirIndex = index; + theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID; + theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB); + + if ( theGlobals->error == noErr ) + { + if ( (theGlobals->copyFilterProc == NULL) || + CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */ + { + /* Either there's no filter proc OR the filter proc says to use this item */ + + /* We have an item. Is it a file or directory? */ + if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* We have a directory */ + + /* Create a new directory at the destination. No errors allowed! */ + theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID); + if ( theGlobals->error == noErr ) + { + /* Save the current source directory ID where we can get it when we come back + ** from recursion land. */ + currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID; + + /* Dive again (copy the directory level we just found below this one) */ + CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals); + + if ( !theGlobals->bailout ) + { + /* Copy comment from old to new directory. */ + /* Ignore the result because we really don't care if it worked or not. */ + (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL); + + /* Copy directory attributes (dates, etc.) to newDirID. */ + /* No errors allowed */ + theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true); + + /* handle any errors from CopyFileMgrAttributes */ + if ( theGlobals->error != noErr ) + { + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp, + theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, + theGlobals->destinationVRefNum, newDirID, NULL); + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + } + } + else /* error handling for DirCreate */ + { + /* note that currentSrcDirID has not been initialised when entering this execution path */ + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp, + theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, + theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName); + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + + if ( !theGlobals->bailout ) + { + /* clear error return on way back if we aren't bailing out */ + theGlobals->error = noErr; + } + } + else + { + /* We have a file, so copy it */ + + theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum, + theGlobals->myCPB.hFileInfo.ioFlParID, + theGlobals->itemName, + theGlobals->destinationVRefNum, + dstDirID, + NULL, + NULL, + theGlobals->copyBuffer, + theGlobals->bufferSize, + false); + + /* handle any errors from FileCopy */ + if ( theGlobals->error != noErr ) + { + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp, + theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName, + theGlobals->destinationVRefNum, dstDirID, NULL); + if ( !theGlobals->bailout ) + { + /* If the CopyErrProc handled the problem, clear the error here */ + theGlobals->error = noErr; + } + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + } + } + } + else + { /* error handling for PBGetCatInfo */ + /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */ + if ( theGlobals->error != fnfErr ) + { + if ( theGlobals->errorHandler != NULL ) + { + theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp, + theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL); + if ( !theGlobals->bailout ) + { + /* If the CopyErrProc handled the problem, clear the error here */ + theGlobals->error = noErr; + } + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + theGlobals->bailout = true; + } + } + } + ++index; /* prepare to get next item */ + } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */ +} + +/*****************************************************************************/ + +pascal OSErr FilteredDirectoryCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc) +{ + EnumerateGlobals theGlobals; + Boolean isDirectory; + OSErr error; + Boolean ourCopyBuffer = false; + Str63 srcDirName, oldDiskName; + Boolean spaceOK; + + /* Make sure a copy buffer is allocated. */ + if ( copyBufferPtr == NULL ) + { + /* The caller didn't supply a copy buffer so grab one from the application heap. + ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer. + ** If 512 bytes aren't available, we're in trouble. */ + copyBufferSize = dirCopyBigCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + copyBufferSize = dirCopyMinCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + return ( memFullErr ); + } + } + ourCopyBuffer = true; + } + + /* Get the real dirID where we're copying from and make sure it is a directory. */ + error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory); + if ( error != noErr ) + { + goto ErrorExit; + } + if ( !isDirectory ) + { + error = dirNFErr; + goto ErrorExit; + } + + /* Special case destination if it is the root parent directory. */ + /* Since you can't create the root directory, this is needed if */ + /* you want to copy a directory's content to a disk's root directory. */ + if ( (dstDirID == fsRtParID) && (dstName == NULL) ) + { + dstDirID = fsRtParID; + isDirectory = true; + error = noErr; + } + else + { + /* Get the real dirID where we're going to put the copy and make sure it is a directory. */ + error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory); + if ( error != noErr ) + { + goto ErrorExit; + } + if ( !isDirectory ) + { + error = dirNFErr; + goto ErrorExit; + } + } + + /* Get the real vRefNum of both the source and destination */ + error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum); + if ( error != noErr ) + { + goto ErrorExit; + } + error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum); + if ( error != noErr ) + { + goto ErrorExit; + } + + if ( preflight ) + { + error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK); + if ( error != noErr ) + { + goto ErrorExit; + } + if ( !spaceOK ) + { + error = dskFulErr; /* not enough room on destination */ + goto ErrorExit; + } + } + + /* Create the new directory in the destination directory with the */ + /* same name as the source directory. */ + error = GetDirName(srcVRefNum, srcDirID, srcDirName); + if ( error != noErr ) + { + goto ErrorExit; + } + + /* Again, special case destination if the destination is the */ + /* root parent directory. This time, we'll rename the disk to */ + /* the source directory name. */ + if ( dstDirID == fsRtParID ) + { + /* Get the current name of the destination disk */ + error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName); + if ( error == noErr ) + { + /* use the copyName as srcDirName if supplied */ + if ( copyName != NULL ) + { + /* make a copy since copyName is a const input */ + BlockMoveData(copyName, srcDirName, sizeof(Str31)); + } + /* Shorten the name if it's too long to be the volume name */ + TruncPString(srcDirName, srcDirName, 27); + + /* Rename the disk */ + error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName); + + /* and copy to the root directory */ + dstDirID = fsRtDirID; + } + } + else + { + /* use the copyName as srcDirName if supplied */ + error = DirCreate(dstVRefNum, dstDirID, ((copyName != NULL) ? copyName : srcDirName), &dstDirID); + } + if ( error != noErr ) + { + /* handle any errors from DirCreate */ + if ( copyErrHandler != NULL ) + { + if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp, + srcVRefNum, srcDirID, NULL, + dstVRefNum, dstDirID, srcDirName) ) + { + goto ErrorExit; + } + else + { + /* If the CopyErrProc handled the problem, clear the error here */ + /* and continue */ + error = noErr; + } + } + else + { + /* If you don't handle the errors with an error handler, */ + /* then the copy stops here. */ + goto ErrorExit; + } + } + + /* dstDirID is now the newly created directory! */ + + /* Set up the globals we need to access from the recursive routine. */ + theGlobals.copyBuffer = (Ptr)copyBufferPtr; + theGlobals.bufferSize = copyBufferSize; + theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */ + theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName; + theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum; + theGlobals.errorHandler = copyErrHandler; + theGlobals.bailout = false; + theGlobals.copyFilterProc = copyFilterProc; + + /* Here we go into recursion land... */ + CopyLevel(srcDirID, dstDirID, &theGlobals); + error = theGlobals.error; /* get the result */ + + if ( !theGlobals.bailout ) + { + /* Copy comment from source to destination directory. */ + /* Ignore the result because we really don't care if it worked or not. */ + (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL); + + /* Copy the File Manager attributes */ + error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL, + dstVRefNum, dstDirID, NULL, true); + + /* handle any errors from CopyFileMgrAttributes */ + if ( (error != noErr) && (copyErrHandler != NULL) ) + { + theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp, + srcVRefNum, srcDirID, NULL, + dstVRefNum, dstDirID, NULL); + } + } + +ErrorExit: + /* Get rid of the copy buffer if we allocated it. */ + if ( ourCopyBuffer ) + { + DisposePtr((Ptr)copyBufferPtr); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DirectoryCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler) +{ + return ( FilteredDirectoryCopy(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, + copyName, + copyBufferPtr, copyBufferSize, preflight, + copyErrHandler, NULL) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpFilteredDirectoryCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc) +{ + return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyName, + copyBufferPtr, copyBufferSize, preflight, + copyErrHandler, copyFilterProc) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpDirectoryCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler) +{ + return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyName, + copyBufferPtr, copyBufferSize, preflight, + copyErrHandler, NULL) ); +} + +/*****************************************************************************/ + diff --git a/src/osx/carbon/morefile/DirectoryCopy.h b/src/osx/carbon/morefile/DirectoryCopy.h new file mode 100644 index 0000000000..14637e31aa --- /dev/null +++ b/src/osx/carbon/morefile/DirectoryCopy.h @@ -0,0 +1,559 @@ +/* + File: DirectoryCopy.h + + Contains: A robust, general purpose directory copy routine. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __DIRECTORYCOPY__ +#define __DIRECTORYCOPY__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +enum { + getNextItemOp = 1, /* couldn't access items in this directory - no access privileges */ + copyDirCommentOp = 2, /* couldn't copy directory's Finder comment */ + copyDirAccessPrivsOp = 3, /* couldn't copy directory's AFP access privileges */ + copyDirFMAttributesOp = 4, /* couldn't copy directory's File Manager attributes */ + dirCreateOp = 5, /* couldn't create destination directory */ + fileCopyOp = 6 /* couldn't copy file */ +}; + + +/*****************************************************************************/ + +typedef CALLBACK_API( Boolean , CopyErrProcPtr )(OSErr error, short failedOperation, short srcVRefNum, long srcDirID, ConstStr255Param srcName, short dstVRefNum, long dstDirID, ConstStr255Param dstName); +/* + This is the prototype for the CopyErrProc function DirectoryCopy + calls if an error condition is detected sometime during the copy. If + CopyErrProc returns false, then DirectoryCopy attempts to continue with + the directory copy operation. If CopyErrProc returns true, then + DirectoryCopy stops the directory copy operation. + + error input: The error result code that caused CopyErrProc to + be called. + failedOperation input: The operation that returned an error to + DirectoryCopy. + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source file or directory name, or nil if + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Destination file or directory name, or nil if + dstDirID specifies the directory. + + __________ + + Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy, DirectoryCopy, FSpDirectoryCopy +*/ +#define CallCopyErrProc(userRoutine, error, failedOperation, srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName) \ + (*(userRoutine))((error), (failedOperation), (srcVRefNum), (srcDirID), (srcName), (dstVRefNum), (dstDirID), (dstName)) + +/*****************************************************************************/ + +typedef CALLBACK_API( Boolean , CopyFilterProcPtr )(const CInfoPBRec * cpbPtr); +/* + This is the prototype for the CopyFilterProc function called by + FilteredDirectoryCopy and GetLevelSize. If true is returned, + the file/folder is included in the copy, otherwise it is excluded. + + pb input: Points to the CInfoPBRec for the item under consideration. + + __________ + + Also see: FilteredDirectoryCopy, FSpFilteredDirectoryCopy +*/ +#define CallCopyFilterProc(userRoutine, cpbPtr) \ + (*(userRoutine))((cpbPtr)) + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FilteredDirectoryCopy( + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + ConstStr255Param copyName, + void * copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc); + + +/* + The FilteredDirectoryCopy function makes a copy of a directory + structure in a new location. If copyBufferPtr <> NIL, it points to + a buffer of copyBufferSize that is used to copy files data. The + larger the supplied buffer, the faster the copy. If + copyBufferPtr = NIL, then this routine allocates a buffer in the + application heap. If you pass a copy buffer to this routine, make + its size a multiple of 512 ($200) bytes for optimum performance. + + The optional copyFilterProc parameter lets a routine you define + decide what files or directories are copied to the destination. + + FilteredDirectoryCopy normally creates a new directory *in* the + specified destination directory and copies the source directory's + content into the new directory. However, if root parent directory + (fsRtParID) is passed as the dstDirID parameter and NULL is + passed as the dstName parameter, DirectoryCopy renames the + destination volume to the source directory's name (truncating + if the name is longer than 27 characters) and copies the source + directory's content into the destination volume's root directory. + This special case is supported by FilteredDirectoryCopy, but + not by FSpFilteredDirectoryCopy since with FSpFilteredDirectoryCopy, + the dstName parameter can not be NULL. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source directory name, or nil if + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Destination directory name, or nil if + dstDirID specifies the directory. + copyName input: Points to the new directory name if the directory + is to be renamed or nil if the directory isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, DirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + copyFilterProc input: A pointer to the filter routine you want called + for each item in the source directory, or NULL + if you don't want to filter. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, CopyFilterProcPtr, FSpFilteredDirectoryCopy, + DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpFilteredDirectoryCopy( + const FSSpec * srcSpec, + const FSSpec * dstSpec, + ConstStr255Param copyName, + void * copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler, + CopyFilterProcPtr copyFilterProc); + + +/* + The FSpFilteredDirectoryCopy function makes a copy of a directory + structure in a new location. If copyBufferPtr <> NIL, it points to + a buffer of copyBufferSize that is used to copy files data. The + larger the supplied buffer, the faster the copy. If + copyBufferPtr = NIL, then this routine allocates a buffer in the + application heap. If you pass a copy buffer to this routine, make + its size a multiple of 512 ($200) bytes for optimum performance. + + The optional copyFilterProc parameter lets a routine you define + decide what files or directories are copied to the destination. + + srcSpec input: An FSSpec record specifying the directory to copy. + dstSpec input: An FSSpec record specifying destination directory + of the copy. + copyName input: Points to the new directory name if the directory + is to be renamed or nil if the directory isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FSpDirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + copyFilterProc input: A pointer to the filter routine you want called + for each item in the source directory, or NULL + if you don't want to filter. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, CopyFilterProcPtr, FilteredDirectoryCopy, + DirectoryCopy, FSpDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DirectoryCopy( + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + ConstStr255Param copyName, + void * copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler); + + +/* + The DirectoryCopy function makes a copy of a directory structure in a + new location. If copyBufferPtr <> NIL, it points to a buffer of + copyBufferSize that is used to copy files data. The larger the + supplied buffer, the faster the copy. If copyBufferPtr = NIL, then this + routine allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + DirectoryCopy normally creates a new directory *in* the specified + destination directory and copies the source directory's content into + the new directory. However, if root parent directory (fsRtParID) + is passed as the dstDirID parameter and NULL is passed as the + dstName parameter, DirectoryCopy renames the destination volume to + the source directory's name (truncating if the name is longer than + 27 characters) and copies the source directory's content into the + destination volume's root directory. This special case is supported + by DirectoryCopy, but not by FSpDirectoryCopy since with + FSpDirectoryCopy, the dstName parameter can not be NULL. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source directory name, or nil if + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Destination directory name, or nil if + dstDirID specifies the directory. + copyName input: Points to the new directory name if the directory + is to be renamed or nil if the directory isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, DirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, FSpDirectoryCopy, FilteredDirectoryCopy, + FSpFilteredDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDirectoryCopy( + const FSSpec * srcSpec, + const FSSpec * dstSpec, + ConstStr255Param copyName, + void * copyBufferPtr, + long copyBufferSize, + Boolean preflight, + CopyErrProcPtr copyErrHandler); + + +/* + The FSpDirectoryCopy function makes a copy of a directory structure in a + new location. If copyBufferPtr <> NIL, it points to a buffer of + copyBufferSize that is used to copy files data. The larger the + supplied buffer, the faster the copy. If copyBufferPtr = NIL, then this + routine allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + srcSpec input: An FSSpec record specifying the directory to copy. + dstSpec input: An FSSpec record specifying destination directory + of the copy. + copyName input: Points to the new directory name if the directory + is to be renamed or nil if the directory isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want DirectoryCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FSpDirectoryCopy makes sure there are + enough allocation blocks on the destination + volume to hold the directory's files before + starting the copy. + copyErrHandler input: A pointer to the routine you want called if an + error condition is detected during the copy, or + nil if you don't want to handle error conditions. + If you don't handle error conditions, the first + error will cause the copy to quit and + DirectoryCopy will return the error. + Error handling is recommended... + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: CopyErrProcPtr, DirectoryCopy, FilteredDirectoryCopy, + FSpFilteredDirectoryCopy, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __DIRECTORYCOPY__ */ + diff --git a/src/osx/carbon/morefile/FSpCompat.c b/src/osx/carbon/morefile/FSpCompat.c new file mode 100644 index 0000000000..17577684f6 --- /dev/null +++ b/src/osx/carbon/morefile/FSpCompat.c @@ -0,0 +1,946 @@ +/* + File: FSpCompat.c + + Contains: FSSpec compatibility functions. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. Updated + various routines to use new calling convention of the + MoreFilesExtras accessor functions. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +/* +** If building application 68K code, set GENERATENODATA to 0 for faster code. +** If building stand-alone 68K code, set GENERATENODATA to 1 so globals +** (static variables) are not used. +*/ +#ifndef GENERATENODATA +#define GENERATENODATA 0 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFilesExtras.h" +#include "FSpCompat.h" + +/*****************************************************************************/ + +/* local constants */ + +enum { + gestaltBugFixAttrsTwo = 'bugy', + gestaltFSpExchangeFilesCompatibilityFix = 26, + gestaltBugFixAttrsThree = 'bugx', + gestaltFSpCreateScriptSupportFix = 1 +}; + +/*****************************************************************************/ + +/* static prototypes */ + + +#if !__MACOSSEVENORLATER +static Boolean FSHasFSSpecCalls(void); + +static Boolean QTHasFSSpecCalls(void); +#endif /* !__MACOSSEVENORLATER */ + +#if !__MACOSSEVENFIVEORLATER +static Boolean HasFSpExchangeFilesCompatibilityFix(void); + +static OSErr GenerateUniqueName(short volume, + long *startSeed, + long dir1, + long dir2, + StringPtr uniqueName); +#endif /* !__MACOSSEVENFIVEORLATER */ + +#if !__MACOSSEVENFIVEONEORLATER +static Boolean HasFSpCreateScriptSupportFix(void); +#endif /* !__MACOSSEVENFIVEONEORLATER */ + +/*****************************************************************************/ + +/* FSHasFSSpecCalls returns true if the file system provides FSSpec calls. */ + +#if !__MACOSSEVENORLATER +static Boolean FSHasFSSpecCalls(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else + Boolean result = false; +#endif + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif + if ( Gestalt(gestaltFSAttr, &response) == noErr ) + { + result = ((response & (1L << gestaltHasFSSpecCalls)) != 0); + } +#if !GENERATENODATA + } +#endif + return ( result ); +} +#endif /* !__MACOSSEVENORLATER */ + +/*****************************************************************************/ + +/* QTHasFSSpecCalls returns true if QuickTime provides FSSpec calls */ +/* except for FSpExchangeFiles. */ + +#if !__MACOSSEVENORLATER +static Boolean QTHasFSSpecCalls(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else + Boolean result = false; +#endif + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif + result = (Gestalt(gestaltQuickTimeVersion, &response) == noErr); +#if !GENERATENODATA + } +#endif + return ( result ); +} +#endif /* !__MACOSSEVENORLATER */ + +/*****************************************************************************/ + +/* HasFSpExchangeFilesCompatibilityFix returns true if FSpExchangeFiles */ +/* compatibility code has been fixed in system software. */ +/* This was fixed by System Update 3.0, so if SystemSevenFiveOrLater */ +/* is true, then we know the fix is in. */ + +#if !__MACOSSEVENFIVEORLATER +static Boolean HasFSpExchangeFilesCompatibilityFix(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else /* !GENERATENODATA */ + Boolean result = false; +#endif /* !GENERATENODATA */ + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif /* !GENERATENODATA */ + if ( Gestalt(gestaltBugFixAttrsTwo, &response) == noErr ) + { + result = ((response & (1L << gestaltFSpExchangeFilesCompatibilityFix)) != 0); + } +#if !GENERATENODATA + } +#endif /* !GENERATENODATA */ + return ( result ); +} +#endif /* !__MACOSSEVENFIVEORLATER */ + +/*****************************************************************************/ + +/* HasFSpCreateScriptSupportFix returns true if FSpCreate and */ +/* FSpCreateResFile have been fixed in system software to correctly set */ +/* the scriptCode in the volume's catalog. */ +/* This was fixed by System 7.5 Update 1.0 */ + +#if !__MACOSSEVENFIVEONEORLATER +static Boolean HasFSpCreateScriptSupportFix(void) +{ + long response; +#if !GENERATENODATA + static Boolean tested = false; + static Boolean result = false; +#else + Boolean result = false; +#endif /* !GENERATENODATA */ + +#if !GENERATENODATA + if ( !tested ) + { + tested = true; +#endif /* !GENERATENODATA */ + if ( Gestalt(gestaltBugFixAttrsThree, &response) == noErr ) + { + result = ((response & (1L << gestaltFSpCreateScriptSupportFix)) != 0); + } +#if !GENERATENODATA + } +#endif /* !GENERATENODATA */ + return ( result ); +} +#endif /* !__MACOSSEVENFIVEONEORLATER */ + +/*****************************************************************************/ + +/* +** File Manager FSp calls +*/ + +/*****************************************************************************/ + +pascal OSErr FSMakeFSSpecCompat(short vRefNum, + long dirID, + ConstStr255Param fileName, + FSSpec *spec) +{ + OSErr result; + +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + Boolean isDirectory; + + result = GetObjectLocation(vRefNum, dirID, fileName, + &(spec->vRefNum), &(spec->parID), spec->name, + &isDirectory); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + /* Let the file system create the FSSpec if it can since it does the job */ + /* much more efficiently than I can. */ + result = FSMakeFSSpec(vRefNum, dirID, fileName, spec); + + /* Fix a bug in Macintosh PC Exchange's MakeFSSpec code where 0 is */ + /* returned in the parID field when making an FSSpec to the volume's */ + /* root directory by passing a full pathname in MakeFSSpec's */ + /* fileName parameter. Fixed in Mac OS 8.1 */ + if ( (result == noErr) && (spec->parID == 0) ) + spec->parID = fsRtParID; + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenDFCompat(const FSSpec *spec, + char permission, + short *refNum) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + pb.ioParam.ioPermssn = permission; + pb.ioParam.ioMisc = NULL; + result = PBHOpenSync(&pb); /* OpenDF not supported by System 6, so use Open */ + *refNum = pb.ioParam.ioRefNum; + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpOpenDF(spec, permission, refNum) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenRFCompat(const FSSpec *spec, + char permission, + short *refNum) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + pb.ioParam.ioPermssn = permission; + pb.ioParam.ioMisc = NULL; + result = PBHOpenRFSync(&pb); + *refNum = pb.ioParam.ioRefNum; + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpOpenRF(spec, permission, refNum) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpCreateCompat(const FSSpec *spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag) +{ +#if !__MACOSSEVENFIVEONEORLATER + OSErr result; + UniversalFMPB pb; + + + if ( +#if !__MACOSSEVENORLATER + (!FSHasFSSpecCalls() && !QTHasFSSpecCalls()) || +#endif /* !__MACOSSEVENORLATER */ + !HasFSpCreateScriptSupportFix() ) + { + /* If FSpCreate isn't called, this code will be executed */ + pb.hPB.fileParam.ioVRefNum = spec->vRefNum; + pb.hPB.fileParam.ioDirID = spec->parID; + pb.hPB.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.hPB.fileParam.ioFVersNum = 0; + result = PBHCreateSync(&(pb.hPB)); + if ( result == noErr ) + { + /* get info on created item */ + pb.ciPB.hFileInfo.ioFDirIndex = 0; + result = PBGetCatInfoSync(&(pb.ciPB)); + if ( result == noErr ) + { + /* Set fdScript in FXInfo */ + /* The negative script constants (smSystemScript, smCurrentScript, and smAllScripts) */ + /* don't make sense on disk, so only use scriptTag if scriptTag >= smRoman */ + /* (smRoman is 0). fdScript is valid if high bit is set (see IM-6, page 9-38) */ + pb.ciPB.hFileInfo.ioFlXFndrInfo.fdScript = (scriptTag >= smRoman) ? + ((char)scriptTag | (char)0x80) : + (smRoman); + /* Set creator/fileType */ + pb.ciPB.hFileInfo.ioFlFndrInfo.fdCreator = creator; + pb.ciPB.hFileInfo.ioFlFndrInfo.fdType = fileType; + /* Restore ioDirID field in pb which was changed by PBGetCatInfo */ + pb.ciPB.hFileInfo.ioDirID = spec->parID; + result = PBSetCatInfoSync(&(pb.ciPB)); + } + } + return ( result ); + } + else +#endif /* !__MACOSSEVENFIVEONEORLATER */ + { + return ( FSpCreate(spec, creator, fileType, scriptTag) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpDirCreateCompat(const FSSpec *spec, + ScriptCode scriptTag, + long *createdDirID) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + UniversalFMPB pb; + + pb.hPB.fileParam.ioVRefNum = spec->vRefNum; + pb.hPB.fileParam.ioDirID = spec->parID; + pb.hPB.fileParam.ioNamePtr = (StringPtr) &(spec->name); + result = PBDirCreateSync(&(pb.hPB)); + *createdDirID = pb.hPB.fileParam.ioDirID; + if ( result == noErr ) + { + /* get info on created item */ + pb.ciPB.dirInfo.ioFDirIndex = 0; + pb.ciPB.dirInfo.ioDrDirID = spec->parID; + result = PBGetCatInfoSync(&(pb.ciPB)); + if ( result == noErr ) + { + /* Set frScript in DXInfo */ + /* The negative script constants (smSystemScript, smCurrentScript, and smAllScripts) */ + /* don't make sense on disk, so only use scriptTag if scriptTag >= smRoman */ + /* (smRoman is 0). frScript is valid if high bit is set (see IM-6, page 9-38) */ + pb.ciPB.dirInfo.ioDrFndrInfo.frScript = (scriptTag >= smRoman) ? + ((char)scriptTag | (char)0x80) : + (smRoman); + /* Restore ioDirID field in pb which was changed by PBGetCatInfo */ + pb.ciPB.dirInfo.ioDrDirID = spec->parID; + result = PBSetCatInfoSync(&(pb.ciPB)); + } + } + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpDirCreate(spec, scriptTag, createdDirID) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpDeleteCompat(const FSSpec *spec) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + return ( PBHDeleteSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpDelete(spec) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFInfoCompat(const FSSpec *spec, + FInfo *fndrInfo) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioFDirIndex = 0; + result = PBHGetFInfoSync(&pb); + *fndrInfo = pb.fileParam.ioFlFndrInfo; + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpGetFInfo(spec, fndrInfo) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpSetFInfoCompat(const FSSpec *spec, + const FInfo *fndrInfo) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + OSErr result; + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioFDirIndex = 0; + result = PBHGetFInfoSync(&pb); + if ( result == noErr ) + { + pb.fileParam.ioFlFndrInfo = *fndrInfo; + pb.fileParam.ioDirID = spec->parID; + result = PBHSetFInfoSync(&pb); + } + return ( result ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpSetFInfo(spec, fndrInfo) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpSetFLockCompat(const FSSpec *spec) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + return ( PBHSetFLockSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpSetFLock(spec) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpRstFLockCompat(const FSSpec *spec) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.fileParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.fileParam.ioNamePtr = (StringPtr) &(spec->name); + pb.fileParam.ioFVersNum = 0; + return ( PBHRstFLockSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpRstFLock(spec) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpRenameCompat(const FSSpec *spec, + ConstStr255Param newName) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + HParamBlockRec pb; + + pb.ioParam.ioVRefNum = spec->vRefNum; + pb.fileParam.ioDirID = spec->parID; + pb.ioParam.ioNamePtr = (StringPtr) &(spec->name); + pb.ioParam.ioVersNum = 0; + pb.ioParam.ioMisc = (Ptr) newName; + return ( PBHRenameSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpRename(spec, newName) ); + } +} + +/*****************************************************************************/ + +pascal OSErr FSpCatMoveCompat(const FSSpec *source, + const FSSpec *dest) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + CMovePBRec pb; + + /* source and destination volume must be the same */ + if ( source->vRefNum != dest->vRefNum ) + return ( paramErr ); + + pb.ioNamePtr = (StringPtr) &(source->name); + pb.ioVRefNum = source->vRefNum; + pb.ioDirID = source->parID; + pb.ioNewDirID = dest->parID; + pb.ioNewName = (StringPtr) &(dest->name); + return ( PBCatMoveSync(&pb) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpCatMove(source, dest) ); + } +} + +/*****************************************************************************/ + +/* GenerateUniqueName generates a name that is unique in both dir1 and dir2 */ +/* on the specified volume. Ripped off from Feldman's code. */ + +#if !__MACOSSEVENFIVEORLATER +static OSErr GenerateUniqueName(short volume, + long *startSeed, + long dir1, + long dir2, + StringPtr uniqueName) +{ + OSErr error = noErr; + long i; + CInfoPBRec cinfo; + unsigned char hexStr[16]; + + for ( i = 0; i < 16; ++i ) + { + if ( i < 10 ) + { + hexStr[i] = 0x30 + i; + } + else + { + hexStr[i] = 0x37 + i; + } + } + + cinfo.hFileInfo.ioVRefNum = volume; + cinfo.hFileInfo.ioFDirIndex = 0; + cinfo.hFileInfo.ioNamePtr = uniqueName; + + while ( error != fnfErr ) + { + (*startSeed)++; + cinfo.hFileInfo.ioNamePtr[0] = 8; + for ( i = 1; i <= 8; i++ ) + { + cinfo.hFileInfo.ioNamePtr[i] = hexStr[((*startSeed >> ((8-i)*4)) & 0xf)]; + } + cinfo.hFileInfo.ioDirID = dir1; + error = fnfErr; + for ( i = 1; i <= 2; i++ ) + { + error = error & PBGetCatInfoSync(&cinfo); + cinfo.hFileInfo.ioDirID = dir2; + if ( (error != fnfErr) && (error != noErr) ) + { + return ( error ); + } + } + } + return ( noErr ); +} +#endif /* !__MACOSSEVENFIVEORLATER */ + +/*****************************************************************************/ + +pascal OSErr FSpExchangeFilesCompat(const FSSpec *source, + const FSSpec *dest) +{ +#if !__MACOSSEVENFIVEORLATER + if ( +#if !__MACOSSEVENORLATER + !FSHasFSSpecCalls() || +#endif /* !__MACOSSEVENORLATER */ + !HasFSpExchangeFilesCompatibilityFix() ) + { + HParamBlockRec pb; + CInfoPBRec catInfoSource, catInfoDest; + OSErr result, result2; + Str31 unique1, unique2; + StringPtr unique1Ptr, unique2Ptr, swapola; + GetVolParmsInfoBuffer volInfo; + long theSeed, temp; + + /* Make sure the source and destination are on the same volume */ + if ( source->vRefNum != dest->vRefNum ) + { + result = diffVolErr; + goto errorExit3; + } + + /* Try PBExchangeFiles first since it preserves the file ID reference */ + pb.fidParam.ioNamePtr = (StringPtr) &(source->name); + pb.fidParam.ioVRefNum = source->vRefNum; + pb.fidParam.ioDestNamePtr = (StringPtr) &(dest->name); + pb.fidParam.ioDestDirID = dest->parID; + pb.fidParam.ioSrcDirID = source->parID; + + result = PBExchangeFilesSync(&pb); + + /* Note: The compatibility case won't work for files with *Btree control blocks. */ + /* Right now the only *Btree files are created by the system. */ + if ( result != noErr ) + { + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioBuffer = (Ptr) &volInfo; + pb.ioParam.ioReqCount = sizeof(volInfo); + result2 = PBHGetVolParmsSync(&pb); + + /* continue if volume has no fileID support (or no GetVolParms support) */ + if ( (result2 == noErr) && hasFileIDs(&volInfo) ) + { + goto errorExit3; + } + + /* Get the catalog information for each file */ + /* and make sure both files are *really* files */ + catInfoSource.hFileInfo.ioVRefNum = source->vRefNum; + catInfoSource.hFileInfo.ioFDirIndex = 0; + catInfoSource.hFileInfo.ioNamePtr = (StringPtr) &(source->name); + catInfoSource.hFileInfo.ioDirID = source->parID; + catInfoSource.hFileInfo.ioACUser = 0; /* ioACUser used to be filler2 */ + result = PBGetCatInfoSync(&catInfoSource); + if ( result != noErr ) + { + goto errorExit3; + } + if ( (catInfoSource.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + result = notAFileErr; + goto errorExit3; + } + + catInfoDest.hFileInfo.ioVRefNum = dest->vRefNum; + catInfoDest.hFileInfo.ioFDirIndex = 0; + catInfoDest.hFileInfo.ioNamePtr = (StringPtr) &(dest->name); + catInfoDest.hFileInfo.ioDirID = dest->parID; + catInfoDest.hFileInfo.ioACUser = 0; /* ioACUser used to be filler2 */ + result = PBGetCatInfoSync(&catInfoDest); + if ( result != noErr ) + { + goto errorExit3; + } + if ( (catInfoDest.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + result = notAFileErr; + goto errorExit3; + } + + /* generate 2 filenames that are unique in both directories */ + theSeed = 0x64666A6C; /* a fine unlikely filename */ + unique1Ptr = (StringPtr)&unique1; + unique2Ptr = (StringPtr)&unique2; + + result = GenerateUniqueName(source->vRefNum, &theSeed, source->parID, dest->parID, unique1Ptr); + if ( result != noErr ) + { + goto errorExit3; + } + + GenerateUniqueName(source->vRefNum, &theSeed, source->parID, dest->parID, unique2Ptr); + if ( result != noErr ) + { + goto errorExit3; + } + + /* rename source to unique1 */ + pb.fileParam.ioNamePtr = (StringPtr) &(source->name); + pb.ioParam.ioMisc = (Ptr) unique1Ptr; + pb.ioParam.ioVersNum = 0; + result = PBHRenameSync(&pb); + if ( result != noErr ) + { + goto errorExit3; + } + + /* rename dest to unique2 */ + pb.ioParam.ioMisc = (Ptr) unique2Ptr; + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr) &(dest->name); + pb.fileParam.ioDirID = dest->parID; + result = PBHRenameSync(&pb); + if ( result != noErr ) + { + goto errorExit2; /* back out gracefully by renaming unique1 back to source */ + } + + /* If files are not in same directory, swap their locations */ + if ( source->parID != dest->parID ) + { + /* move source file to dest directory */ + pb.copyParam.ioNamePtr = unique1Ptr; + pb.copyParam.ioNewName = NULL; + pb.copyParam.ioNewDirID = dest->parID; + pb.copyParam.ioDirID = source->parID; + result = PBCatMoveSync((CMovePBPtr) &pb); + if ( result != noErr ) + { + goto errorExit1; /* back out gracefully by renaming both files to original names */ + } + + /* move dest file to source directory */ + pb.copyParam.ioNamePtr = unique2Ptr; + pb.copyParam.ioNewDirID = source->parID; + pb.copyParam.ioDirID = dest->parID; + result = PBCatMoveSync((CMovePBPtr) &pb); + if ( result != noErr) + { + /* life is very bad. We'll at least try to move source back */ + pb.copyParam.ioNamePtr = unique1Ptr; + pb.copyParam.ioNewName = NULL; + pb.copyParam.ioNewDirID = source->parID; + pb.copyParam.ioDirID = dest->parID; + (void) PBCatMoveSync((CMovePBPtr) &pb); /* ignore errors */ + goto errorExit1; /* back out gracefully by renaming both files to original names */ + } + } + + /* Make unique1Ptr point to file in source->parID */ + /* and unique2Ptr point to file in dest->parID */ + /* This lets us fall through to the rename code below */ + swapola = unique1Ptr; + unique1Ptr = unique2Ptr; + unique2Ptr = swapola; + + /* At this point, the files are in their new locations (if they were moved) */ + /* Source is named Unique1 (name pointed to by unique2Ptr) and is in dest->parID */ + /* Dest is named Unique2 (name pointed to by unique1Ptr) and is in source->parID */ + /* Need to swap attributes except mod date and swap names */ + + /* swap the catalog info by re-aiming the CInfoPB's */ + catInfoSource.hFileInfo.ioNamePtr = unique1Ptr; + catInfoDest.hFileInfo.ioNamePtr = unique2Ptr; + + catInfoSource.hFileInfo.ioDirID = source->parID; + catInfoDest.hFileInfo.ioDirID = dest->parID; + + /* Swap the original mod dates with each file */ + temp = catInfoSource.hFileInfo.ioFlMdDat; + catInfoSource.hFileInfo.ioFlMdDat = catInfoDest.hFileInfo.ioFlMdDat; + catInfoDest.hFileInfo.ioFlMdDat = temp; + + /* Here's the swap (ignore errors) */ + (void) PBSetCatInfoSync(&catInfoSource); + (void) PBSetCatInfoSync(&catInfoDest); + + /* rename unique2 back to dest */ +errorExit1: + pb.ioParam.ioMisc = (Ptr) &(dest->name); + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioNamePtr = unique2Ptr; + pb.fileParam.ioDirID = dest->parID; + (void) PBHRenameSync(&pb); /* ignore errors */ + + /* rename unique1 back to source */ +errorExit2: + pb.ioParam.ioMisc = (Ptr) &(source->name); + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioNamePtr = unique1Ptr; + pb.fileParam.ioDirID = source->parID; + (void) PBHRenameSync(&pb); /* ignore errors */ + } +errorExit3: { /* null statement */ } + return ( result ); + } + else +#endif /* !__MACOSSEVENFIVEORLATER */ + { + return ( FSpExchangeFiles(source, dest) ); + } +} + +/*****************************************************************************/ + +/* +** Resource Manager FSp calls +*/ + +/*****************************************************************************/ + +pascal short FSpOpenResFileCompat(const FSSpec *spec, + SignedByte permission) +{ +#if !__MACOSSEVENORLATER + if ( !FSHasFSSpecCalls() && !QTHasFSSpecCalls() ) + { + return ( HOpenResFile(spec->vRefNum, spec->parID, spec->name, permission) ); + } + else +#endif /* !__MACOSSEVENORLATER */ + { + return ( FSpOpenResFile(spec, permission) ); + } +} + +/*****************************************************************************/ + +pascal void FSpCreateResFileCompat(const FSSpec *spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag) +{ +#if !__MACOSSEVENFIVEONEORLATER + if ( +#if !__MACOSSEVENORLATER + (!FSHasFSSpecCalls() && !QTHasFSSpecCalls()) || +#endif /* !__MACOSSEVENORLATER */ + !HasFSpCreateScriptSupportFix() ) + { + OSErr result; + CInfoPBRec pb; + + HCreateResFile(spec->vRefNum, spec->parID, spec->name); + if ( ResError() == noErr ) + { + /* get info on created item */ + pb.hFileInfo.ioVRefNum = spec->vRefNum; + pb.hFileInfo.ioDirID = spec->parID; + pb.hFileInfo.ioNamePtr = (StringPtr) &(spec->name); + pb.hFileInfo.ioFDirIndex = 0; + result = PBGetCatInfoSync(&pb); + if ( result == noErr ) + { + /* Set fdScript in FXInfo */ + /* The negative script constants (smSystemScript, smCurrentScript, and smAllScripts) */ + /* don't make sense on disk, so only use scriptTag if scriptTag >= smRoman */ + /* (smRoman is 0). fdScript is valid if high bit is set (see IM-6, page 9-38) */ + pb.hFileInfo.ioFlXFndrInfo.fdScript = (scriptTag >= smRoman) ? + ((char)scriptTag | (char)0x80) : + (smRoman); + /* Set creator/fileType */ + pb.hFileInfo.ioFlFndrInfo.fdCreator = creator; + pb.hFileInfo.ioFlFndrInfo.fdType = fileType; + + /* Restore ioDirID field in pb which was changed by PBGetCatInfo */ + pb.hFileInfo.ioDirID = spec->parID; + result = PBSetCatInfoSync(&pb); + } + /* Set ResErr low memory global to result */ + LMSetResErr(result); + } + return; + } + else +#endif /* !__MACOSSEVENFIVEONEORLATER */ + { + FSpCreateResFile(spec, creator, fileType, scriptTag); + return; + } +} + +/*****************************************************************************/ diff --git a/src/osx/carbon/morefile/FSpCompat.h b/src/osx/carbon/morefile/FSpCompat.h new file mode 100644 index 0000000000..1014cb8e58 --- /dev/null +++ b/src/osx/carbon/morefile/FSpCompat.h @@ -0,0 +1,586 @@ +/* + File: FSpCompat.h + + Contains: FSSpec compatibility functions. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __FSPCOMPAT__ +#define __FSPCOMPAT__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSMakeFSSpecCompat( + short vRefNum, + long dirID, + ConstStr255Param fileName, + FSSpec * spec); + + +/* + The FSMakeFSSpecCompat function fills in the fields of an FSSpec record. + If the file system can't create the FSSpec, then the compatibility code + creates a FSSpec that is exactly like an FSSpec except that spec.name + for a file may not have the same capitalization as the file's catalog + entry on the disk volume. That is because fileName is parsed to get the + name instead of getting the name back from the file system. This works + fine with System 6 where FSMakeSpec isn't available. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + spec output: A file system specification to be filled in by + FSMakeFSSpecCompat. + + Result Codes + noErr 0 No error + nsvErr -35 Volume doesnÕt exist + fnfErr -43 File or directory does not exist + (FSSpec is still valid) +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpOpenDFCompat( + const FSSpec * spec, + char permission, + short * refNum); + + +/* + The FSpOpenDFCompat function opens the data fork of the file specified + by spec. + Differences from FSpOpenDF: If FSpOpenDF isn't available, + FSpOpenDFCompat uses PHBOpen because System 6 doesn't support PBHOpenDF. + This means FSpOpenDFCompat could accidentally open a driver if the + spec->name begins with a period. + + spec input: An FSSpec record specifying the file whose data + fork is to be opened. + permission input: A constant indicating the desired file access + permissions. + refNum output: A reference number of an access path to the file's + data fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 File not found + opWrErr -49 File already open for writing + permErr -54 Attempt to open locked file for writing + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSpOpenAware +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpOpenRFCompat( + const FSSpec * spec, + char permission, + short * refNum); + + +/* + The FSpOpenRFCompat function opens the resource fork of the file + specified by spec. + + spec input: An FSSpec record specifying the file whose resource + fork is to be opened. + permission input: A constant indicating the desired file access + permissions. + refNum output: A reference number of an access path to the file's + resource fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 File not found + opWrErr -49 File already open for writing + permErr -54 Attempt to open locked file for writing + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSpOpenRFAware +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCreateCompat( + const FSSpec * spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag); + + +/* + The FSpCreateCompat function creates a new file with the specified + type, creator, and script code. + Differences from FSpCreate: FSpCreateCompat correctly sets the + fdScript in the file's FXInfo record to scriptTag if the problem + isn't fixed in the File Manager code. + + spec input: An FSSpec record specifying the file to create. + creator input: The creator of the new file. + fileType input The file type of the new file. + scriptCode input: The code of the script system in which the file + name is to be displayed. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 A directory exists with that name +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDirCreateCompat( + const FSSpec * spec, + ScriptCode scriptTag, + long * createdDirID); + + +/* + The FSpDirCreateCompat function creates a new directory and returns the + directory ID of the newDirectory. + + spec input: An FSSpec record specifying the directory to + create. + scriptCode input: The code of the script system in which the + directory name is to be displayed. + createdDirID output: The directory ID of the directory that was + created. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Not an HFS volume + afpAccessDenied -5000 User does not have the correct access +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDeleteCompat(const FSSpec * spec); + + +/* + The FSpDeleteCompat function deletes a file or directory. + + spec input: An FSSpec record specifying the file or + directory to delete. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + fBsyErr -47 File busy, directory not empty, or + working directory control block open + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetFInfoCompat( + const FSSpec * spec, + FInfo * fndrInfo); + + +/* + The FSpGetFInfoCompat function gets the finder information for a file. + + spec input: An FSSpec record specifying the file. + fndrInfo output: If the object is a file, then its FInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpGetDInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetFInfoCompat( + const FSSpec * spec, + const FInfo * fndrInfo); + + +/* + The FSpSetFInfoCompat function sets the finder information for a file. + + spec input: An FSSpec record specifying the file. + fndrInfo input: The FInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Object was a directory + + __________ + + Also see: FSpSetDInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetFLockCompat(const FSSpec * spec); + + +/* + The FSpSetFLockCompat function locks a file. + + spec input: An FSSpec record specifying the file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + afpObjectTypeErr -5025 Folder locking not supported by volume +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpRstFLockCompat(const FSSpec * spec); + + +/* + The FSpRstFLockCompat function unlocks a file. + + spec input: An FSSpec record specifying the file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file + afpObjectTypeErr -5025 Folder locking not supported by volume +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpRenameCompat( + const FSSpec * spec, + ConstStr255Param newName); + + +/* + The FSpRenameCompat function renames a file or directory. + + spec input: An FSSpec record specifying the file. + newName input: The new name of the file or directory. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + paramErr -50 No default volume + fsRnErr -59 Problem during rename + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCatMoveCompat( + const FSSpec * source, + const FSSpec * dest); + + +/* + The FSpCatMoveCompat function moves a file or directory to a different + location on on the same volume. + + source input: An FSSpec record specifying the file or directory. + dest input: An FSSpec record specifying the name and location + of the directory into which the source file or + directory is to be moved. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename or attempt to move into + a file + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 Target directory is locked + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + paramErr -50 No default volume + badMovErr -122 Attempt to move into offspring + wrgVolTypErr -123 Not an HFS volume + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpExchangeFilesCompat( + const FSSpec * source, + const FSSpec * dest); + + +/* + The FSpExchangeFilesCompat function swaps the data in two files by + changing the information in the volume's catalog and, if the files + are open, in the file control blocks. + Differences from FSpExchangeFiles: Correctly exchanges files on volumes + that don't support PBExchangeFiles. FSpExchangeFiles attempts to support + volumes that don't support PBExchangeFiles, but in System 7, 7.0.1, 7.1, + and 7 Pro, the compatibility code just doesn't work on volumes that + don't support PBExchangeFiles (even though you may get a noErr result). + System Update 3.0 and System 7.5 and later have the problems in + FSpExchangeFiles corrected. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + wrgVolTypErr -123 Not an HFS volume + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Object is a directory, not a file + afpSameObjectErr -5038 Source and destination files are the same +*/ + +/*****************************************************************************/ + +EXTERN_API( short ) +FSpOpenResFileCompat( + const FSSpec * spec, + SignedByte permission); + + +/* + The FSpOpenResFileCompat function opens the resource file specified + by spec. + + spec input: An FSSpec record specifying the file whose + resource file is to be opened. + permission input: A constant indicating the desired file access + permissions. + function result output: A resource file reference number, or if there's + an error -1. + + Result Codes + noErr 0 No error + nsvErr Ð35 No such volume + ioErr Ð36 I/O error + bdNamErr Ð37 Bad filename or volume name (perhaps zero + length) + eofErr Ð39 End of file + tmfoErr Ð42 Too many files open + fnfErr Ð43 File not found + opWrErr Ð49 File already open with write permission + permErr Ð54 Permissions error (on file open) + extFSErr Ð58 Volume belongs to an external file system + memFullErr Ð108 Not enough room in heap zone + dirNFErr Ð120 Directory not found + mapReadErr Ð199 Map inconsistent with operation +*/ + +/*****************************************************************************/ + +EXTERN_API( void ) +FSpCreateResFileCompat( + const FSSpec * spec, + OSType creator, + OSType fileType, + ScriptCode scriptTag); + + +/* + The FSpCreateResFileCompat function creates a new resource file with + the specified type, creator, and script code. + Differences from FSpCreateResFile: FSpCreateResFileCompat correctly + sets the fdScript in the file's FXInfo record to scriptTag if the + problem isn't fixed in the File Manager code. + + spec input: An FSSpec record specifying the resource file to create. + creator input: The creator of the new file. + fileType input The file type of the new file. + scriptCode input: The code of the script system in which the file + name is to be displayed. + + Result Codes + noErr 0 No error + dirFulErr Ð33 Directory full + dskFulErr Ð34 Disk full + nsvErr Ð35 No such volume + ioErr Ð36 I/O error + bdNamErr Ð37 Bad filename or volume name (perhaps zero + length) + tmfoErr Ð42 Too many files open + wPrErrw Ð44 Disk is write-protected + fLckdErr Ð45 File is locked +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __FSPCOMPAT__ */ + diff --git a/src/osx/carbon/morefile/FileCopy.c b/src/osx/carbon/morefile/FileCopy.c new file mode 100644 index 0000000000..4848613b51 --- /dev/null +++ b/src/osx/carbon/morefile/FileCopy.c @@ -0,0 +1,611 @@ +/* + File: FileCopy.c + + Contains: A robust, general purpose file copy routine. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. Updated + various routines to use new calling convention of the + MoreFilesExtras accessor functions. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFiles.h" +#include "MoreFilesExtras.h" +#include "MoreDesktopMgr.h" +#include "FileCopy.h" + +/*****************************************************************************/ + +/* local constants */ + +/* The deny-mode privileges to use when opening the source and destination files. */ + +enum +{ + srcCopyMode = dmRdDenyWr, + dstCopyMode = dmWrDenyRdWr +}; + +/* The largest (16K) and smallest (.5K) copy buffer to use if the caller doesn't supply +** their own copy buffer. */ + +enum +{ + bigCopyBuffSize = 0x00004000, + minCopyBuffSize = 0x00000200 +}; + +/*****************************************************************************/ + +/* static prototypes */ + +static OSErr GetDestinationDirInfo(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory, + Boolean *isDropBox); +/* GetDestinationDirInfo tells us if the destination is a directory, it's + directory ID, and if it's an AppleShare drop box (write privileges only -- + no read or search privileges). + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + theDirID output: If the object is a file, then its parent directory + ID. If the object is a directory, then its ID. + isDirectory output: True if object is a directory; false if + object is a file. + isDropBox output: True if directory is an AppleShare drop box. +*/ + +static OSErr CheckForForks(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean *hasDataFork, + Boolean *hasResourceFork); +/* CheckForForks tells us if there is a data or resource fork to copy. + vRefNum input: Volume specification of the file's current + location. + dirID input: Directory ID of the file's current location. + name input: The name of the file. +*/ + +static OSErr PreflightFileCopySpace(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + ConstStr255Param dstVolName, + short dstVRefNum, + Boolean *spaceOK); +/* PreflightFileCopySpace determines if there's enough space on a + volume to copy the specified file to that volume. + Note: The results of this routine are not perfect. For example if the + volume's catalog or extents overflow file grows when the new file is + created, more allocation blocks may be needed beyond those needed for + the file's data and resource forks. + + srcVRefNum input: Volume specification of the file's current + location. + srcDirID input: Directory ID of the file's current location. + srcName input: The name of the file. + dstVolName input: A pointer to the name of the volume where + the file will be copied or NULL. + dstVRefNum input: Volume specification indicating the volume + where the file will be copied. + spaceOK output: true if there's enough space on the volume for + the file's data and resource forks. +*/ + +/*****************************************************************************/ + +static OSErr GetDestinationDirInfo(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory, + Boolean *isDropBox) +{ + CInfoPBRec pb; + OSErr error; + + pb.dirInfo.ioACUser = 0; /* ioACUser used to be filler2, clear it before calling GetCatInfo */ + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + *theDirID = pb.dirInfo.ioDrDirID; + *isDirectory = (pb.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0; + /* see if access priviledges are make changes, not see folder, and not see files (drop box) */ + *isDropBox = userHasDropBoxAccess(pb.dirInfo.ioACUser); + + return ( error ); +} + +/*****************************************************************************/ + +static OSErr CheckForForks(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean *hasDataFork, + Boolean *hasResourceFork) +{ + HParamBlockRec pb; + OSErr error; + + pb.fileParam.ioNamePtr = (StringPtr)name; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioDirID = dirID; + pb.fileParam.ioFDirIndex = 0; + error = PBHGetFInfoSync(&pb); + *hasDataFork = (pb.fileParam.ioFlLgLen != 0); + *hasResourceFork = (pb.fileParam.ioFlRLgLen != 0); + + return ( error ); +} + +/*****************************************************************************/ + +static OSErr PreflightFileCopySpace(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + ConstStr255Param dstVolName, + short dstVRefNum, + Boolean *spaceOK) +{ + UniversalFMPB pb; + OSErr error; + unsigned long dstFreeBlocks; + unsigned long dstBlksPerAllocBlk; + unsigned long srcDataBlks; + unsigned long srcResourceBlks; + + error = XGetVolumeInfoNoName(dstVolName, dstVRefNum, &pb.xPB); + if ( error == noErr ) + { + /* get allocation block size (always multiple of 512) and divide by 512 + to get number of 512-byte blocks per allocation block */ + dstBlksPerAllocBlk = ((unsigned long)pb.xPB.ioVAlBlkSiz >> 9); + + /* Convert freeBytes to free disk blocks (512-byte blocks) */ + dstFreeBlocks = U32SetU(U64ShiftRight(pb.xPB.ioVFreeBytes, 9)); + + /* Now, get the size of the file's data resource forks */ + pb.hPB.fileParam.ioNamePtr = (StringPtr)srcName; + pb.hPB.fileParam.ioVRefNum = srcVRefNum; + pb.hPB.fileParam.ioFVersNum = 0; + pb.hPB.fileParam.ioDirID = srcDirID; + pb.hPB.fileParam.ioFDirIndex = 0; + error = PBHGetFInfoSync(&pb.hPB); + if ( error == noErr ) + { + /* Since space on Mac OS disks is always allocated in allocation blocks, */ + /* this code takes into account rounding up to the end of an allocation block. */ + + /* get number of 512-byte blocks needed for data fork */ + if ( ((unsigned long)pb.hPB.fileParam.ioFlLgLen & 0x000001ff) != 0 ) + { + srcDataBlks = ((unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9) + 1; + } + else + { + srcDataBlks = (unsigned long)pb.hPB.fileParam.ioFlLgLen >> 9; + } + + /* now, calculate number of new allocation blocks needed */ + if ( srcDataBlks % dstBlksPerAllocBlk ) + { + srcDataBlks = (srcDataBlks / dstBlksPerAllocBlk) + 1; + } + else + { + srcDataBlks /= dstBlksPerAllocBlk; + } + + /* get number of 512-byte blocks needed for resource fork */ + if ( ((unsigned long)pb.hPB.fileParam.ioFlRLgLen & 0x000001ff) != 0 ) + { + srcResourceBlks = ((unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9) + 1; + } + else + { + srcResourceBlks = (unsigned long)pb.hPB.fileParam.ioFlRLgLen >> 9; + } + + /* now, calculate number of new allocation blocks needed */ + if ( srcResourceBlks % dstBlksPerAllocBlk ) + { + srcResourceBlks = (srcResourceBlks / dstBlksPerAllocBlk) + 1; + } + else + { + srcResourceBlks /= dstBlksPerAllocBlk; + } + + /* Is there enough room on the destination volume for the source file? */ + *spaceOK = ( ((srcDataBlks + srcResourceBlks) * dstBlksPerAllocBlk) <= dstFreeBlocks ); + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FileCopy(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight) +{ + OSErr err; + + short srcRefNum = 0, /* 0 when source data and resource fork are closed */ + dstDataRefNum = 0, /* 0 when destination data fork is closed */ + dstRsrcRefNum = 0; /* 0 when destination resource fork is closed */ + + Str63 dstName; /* The filename of the destination. It might be the + ** source filename, it might be a new name... */ + + GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */ + long srcServerAdr; /* AppleTalk server address of source (if any) */ + + Boolean dstCreated = false, /* true when destination file has been created */ + ourCopyBuffer = false, /* true if we had to allocate the copy buffer */ + isDirectory, /* true if destination is really a directory */ + isDropBox; /* true if destination is an AppleShare drop box */ + + long tempLong; + short tempInt; + + Boolean spaceOK; /* true if there's enough room to copy the file to the destination volume */ + + Boolean hasDataFork; + Boolean hasResourceFork; + + /* Preflight for size */ + if ( preflight ) + { + err = PreflightFileCopySpace(srcVRefNum, srcDirID, srcName, + dstPathname, dstVRefNum, &spaceOK); + if ( err != noErr ) + { + return ( err ); + } + + if ( !spaceOK ) + { + return ( dskFulErr ); + } + } + + /* get the destination's real dirID and make sure it really is a directory */ + err = GetDestinationDirInfo(dstVRefNum, dstDirID, dstPathname, + &dstDirID, &isDirectory, &isDropBox); + if ( err != noErr ) + { + goto ErrorExit; + } + + if ( !isDirectory ) + { + return ( dirNFErr ); + } + + /* get the destination's real vRefNum */ + err = DetermineVRefNum(dstPathname, dstVRefNum, &dstVRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* See if PBHCopyFile can be used. Using PBHCopyFile saves time by letting the file server + ** copy the file if the source and destination locations are on the same file server. */ + tempLong = sizeof(infoBuffer); + err = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong); + if ( (err != noErr) && (err != paramErr) ) + { + return ( err ); + } + + if ( (err != paramErr) && hasCopyFile(&infoBuffer) ) + { + /* The source volume supports PBHCopyFile. */ + srcServerAdr = infoBuffer.vMServerAdr; + + /* Now, see if the destination volume is on the same file server. */ + tempLong = sizeof(infoBuffer); + err = HGetVolParms(NULL, dstVRefNum, &infoBuffer, &tempLong); + if ( (err != noErr) && (err != paramErr) ) + { + return ( err ); + } + if ( (err != paramErr) && (srcServerAdr == infoBuffer.vMServerAdr) ) + { + /* Source and Dest are on same server and PBHCopyFile is supported. Copy with CopyFile. */ + err = HCopyFile(srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, NULL, copyName); + if ( err != noErr ) + { + return ( err ); + } + + /* AppleShare's CopyFile clears the isAlias bit, so I still need to attempt to copy + the File's attributes to attempt to get things right. */ + if ( copyName != NULL ) /* Did caller supply copy file name? */ + { + /* Yes, use the caller supplied copy file name. */ + (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, copyName, true); + } + else + { + /* They didn't, so get the source file name and use it. */ + if ( GetFilenameFromPathname(srcName, dstName) == noErr ) + { + /* */ + (void) CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, true); + } + } + return ( err ); + } + } + + /* If we're here, then PBHCopyFile couldn't be used so we have to copy the file by hand. */ + + /* Make sure a copy buffer is allocated. */ + if ( copyBufferPtr == NULL ) + { + /* The caller didn't supply a copy buffer so grab one from the application heap. + ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer. + ** If 512 bytes aren't available, we're in trouble. */ + copyBufferSize = bigCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + copyBufferSize = minCopyBuffSize; + copyBufferPtr = NewPtr(copyBufferSize); + if ( copyBufferPtr == NULL ) + { + return ( memFullErr ); + } + } + ourCopyBuffer = true; + } + + /* Open the source data fork. */ + err = HOpenAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum); + if ( err != noErr ) + return ( err ); + + /* Once a file is opened, we have to exit via ErrorExit to make sure things are cleaned up */ + + /* See if the copy will be renamed. */ + if ( copyName != NULL ) /* Did caller supply copy file name? */ + BlockMoveData(copyName, dstName, copyName[0] + 1); /* Yes, use the caller supplied copy file name. */ + else + { /* They didn't, so get the source file name and use it. */ + err = GetFileLocation(srcRefNum, &tempInt, &tempLong, dstName); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + /* Create the destination file. */ + err = HCreateMinimum(dstVRefNum, dstDirID, dstName); + if ( err != noErr ) + { + goto ErrorExit; + } + dstCreated = true; /* After creating the destination file, any + ** error conditions should delete the destination file */ + + /* An AppleShare dropbox folder is a folder for which the user has the Make Changes + ** privilege (write access), but not See Files (read access) and See Folders (search access). + ** Copying a file into an AppleShare dropbox presents some special problems. Here are the + ** rules we have to follow to copy a file into a dropbox: + ** ¥ File attributes can be changed only when both forks of a file are empty. + ** ¥ DeskTop Manager comments can be added to a file only when both forks of a file + ** are empty. + ** ¥ A fork can be opened for write access only when both forks of a file are empty. + ** So, with those rules to live with, we'll do those operations now while both forks + ** are empty. */ + + if ( isDropBox ) + { + /* We only set the file attributes now if the file is being copied into a + ** drop box. In all other cases, it is better to set the attributes last + ** so that if FileCopy is modified to give up time to other processes + ** periodicly, the Finder won't try to read any bundle information (because + ** the bundle-bit will still be clear) from a partially copied file. If the + ** copy is into a drop box, we have to set the attributes now, but since the + ** destination forks are opened with write/deny-read/deny-write permissions, + ** any Finder that might see the file in the drop box won't be able to open + ** its resource fork until the resource fork is closed. + ** + ** Note: if you do modify FileCopy to give up time to other processes, don't + ** give up time between the time the destination file is created (above) and + ** the time both forks are opened (below). That way, you stand the best chance + ** of making sure the Finder doesn't read a partially copied resource fork. + */ + /* Copy attributes but don't lock the destination. */ + err = CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, false); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + /* Attempt to copy the comments while both forks are empty. + ** Ignore the result because we really don't care if it worked or not. */ + (void) DTCopyComment(srcVRefNum, srcDirID, srcName, dstVRefNum, dstDirID, dstName); + + /* See which forks we need to copy. By doing this, we won't create a data or resource fork + ** for the destination unless it's really needed (some foreign file systems such as + ** the ProDOS File System and Macintosh PC Exchange have to create additional disk + ** structures to support resource forks). */ + err = CheckForForks(srcVRefNum, srcDirID, srcName, &hasDataFork, &hasResourceFork); + if ( err != noErr ) + { + goto ErrorExit; + } + + if ( hasDataFork ) + { + /* Open the destination data fork. */ + err = HOpenAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstDataRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + if ( hasResourceFork ) + { + /* Open the destination resource fork. */ + err = HOpenRFAware(dstVRefNum, dstDirID, dstName, dstCopyMode, &dstRsrcRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + } + + if ( hasDataFork ) + { + /* Copy the data fork. */ + err = CopyFork(srcRefNum, dstDataRefNum, copyBufferPtr, copyBufferSize); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* Close both data forks and clear reference numbers. */ + (void) FSClose(srcRefNum); + (void) FSClose(dstDataRefNum); + srcRefNum = dstDataRefNum = 0; + } + else + { + /* Close the source data fork since it was opened earlier */ + (void) FSClose(srcRefNum); + srcRefNum = 0; + } + + if ( hasResourceFork ) + { + /* Open the source resource fork. */ + err = HOpenRFAware(srcVRefNum, srcDirID, srcName, srcCopyMode, &srcRefNum); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* Copy the resource fork. */ + err = CopyFork(srcRefNum, dstRsrcRefNum, copyBufferPtr, copyBufferSize); + if ( err != noErr ) + { + goto ErrorExit; + } + + /* Close both resource forks and clear reference numbers. */ + (void) FSClose(srcRefNum); + (void) FSClose(dstRsrcRefNum); + srcRefNum = dstRsrcRefNum = 0; + } + + /* Get rid of the copy buffer if we allocated it. */ + if ( ourCopyBuffer ) + { + DisposePtr((Ptr)copyBufferPtr); + } + + /* Attempt to copy attributes again to set mod date. Copy lock condition this time + ** since we're done with the copy operation. This operation will fail if we're copying + ** into an AppleShare dropbox, so we don't check for error conditions. */ + CopyFileMgrAttributes(srcVRefNum, srcDirID, srcName, + dstVRefNum, dstDirID, dstName, true); + + /* Hey, we did it! */ + return ( noErr ); + +ErrorExit: + if ( srcRefNum != 0 ) + { + (void) FSClose(srcRefNum); /* Close the source file */ + } + if ( dstDataRefNum != 0 ) + { + (void) FSClose(dstDataRefNum); /* Close the destination file data fork */ + } + if ( dstRsrcRefNum != 0 ) + { + (void) FSClose(dstRsrcRefNum); /* Close the destination file resource fork */ + } + if ( dstCreated ) + { + (void) HDelete(dstVRefNum, dstDirID, dstName); /* Delete dest file. This may fail if the file + is in a "drop folder" */ + } + if ( ourCopyBuffer ) /* dispose of any memory we allocated */ + { + DisposePtr((Ptr)copyBufferPtr); + } + + return ( err ); +} + +/*****************************************************************************/ + +pascal OSErr FSpFileCopy(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName, + void *copyBufferPtr, + long copyBufferSize, + Boolean preflight) +{ + return ( FileCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyName, copyBufferPtr, copyBufferSize, preflight) ); +} + +/*****************************************************************************/ + diff --git a/src/osx/carbon/morefile/FileCopy.h b/src/osx/carbon/morefile/FileCopy.h new file mode 100644 index 0000000000..a855f1f96f --- /dev/null +++ b/src/osx/carbon/morefile/FileCopy.h @@ -0,0 +1,271 @@ +/* + File: FileCopy.h + + Contains: A robust, general purpose file copy routine. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __FILECOPY__ +#define __FILECOPY__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FileCopy( + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName, + void * copyBufferPtr, + long copyBufferSize, + Boolean preflight); + + +/* + The FileCopy function duplicates a file and optionally renames it. + Since the PBHCopyFile routine is only available on some + AFP server volumes under specific conditions, this routine + either uses PBHCopyFile, or does all of the work PBHCopyFile + does. The srcVRefNum, srcDirID and srcName are used to + determine the location of the file to copy. The dstVRefNum + dstDirID and dstPathname are used to determine the location of + the destination directory. If copyName <> NIL, then it points + to the name of the new file. If copyBufferPtr <> NIL, it + points to a buffer of copyBufferSize that is used to copy + the file's data. The larger the supplied buffer, the + faster the copy. If copyBufferPtr = NIL, then this routine + allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source file name. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstPathname input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new file name if the file is + to be renamed or nil if the file isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want FileCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FileCopy makes sure there are enough + allocation blocks on the destination volume to + hold both the data and resource forks before + starting the copy. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: FSpFileCopy, DirectoryCopy, FSpDirectoryCopy +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpFileCopy( + const FSSpec * srcSpec, + const FSSpec * dstSpec, + ConstStr255Param copyName, + void * copyBufferPtr, + long copyBufferSize, + Boolean preflight); + + +/* + The FSpFileCopy function duplicates a file and optionally renames it. + Since the PBHCopyFile routine is only available on some + AFP server volumes under specific conditions, this routine + either uses PBHCopyFile, or does all of the work PBHCopyFile + does. The srcSpec is used to + determine the location of the file to copy. The dstSpec is + used to determine the location of the + destination directory. If copyName <> NIL, then it points + to the name of the new file. If copyBufferPtr <> NIL, it + points to a buffer of copyBufferSize that is used to copy + the file's data. The larger the supplied buffer, the + faster the copy. If copyBufferPtr = NIL, then this routine + allocates a buffer in the application heap. If you pass a + copy buffer to this routine, make its size a multiple of 512 + ($200) bytes for optimum performance. + + srcSpec input: An FSSpec record specifying the source file. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new file name if the file is + to be renamed or nil if the file isn't to + be renamed. + copyBufferPtr input: Points to a buffer of copyBufferSize that + is used the i/o buffer for the copy or + nil if you want FileCopy to allocate its + own buffer in the application heap. + copyBufferSize input: The size of the buffer pointed to + by copyBufferPtr. + preflight input: If true, FSpFileCopy makes sure there are + enough allocation blocks on the destination + volume to hold both the data and resource forks + before starting the copy. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Destination volume is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + tmfoErr -42 Too many files open + fnfErr -43 Source file not found, or destination + directory does not exist + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + opWrErr -49 File already open for writing + paramErr -50 No default volume or function not + supported by volume + permErr -54 File is already open and cannot be opened using specified deny modes + memFullErr -108 Copy buffer could not be allocated + dirNFErr -120 Directory not found or incomplete pathname + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory, directory not found + or incomplete pathname + + __________ + + Also see: FileCopy, DirectoryCopy, FSpDirectoryCopy +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __FILECOPY__ */ + diff --git a/src/osx/carbon/morefile/FullPath.c b/src/osx/carbon/morefile/FullPath.c new file mode 100644 index 0000000000..32ccb9977b --- /dev/null +++ b/src/osx/carbon/morefile/FullPath.c @@ -0,0 +1,282 @@ +/* + File: FullPath.c + + Contains: Routines for dealing with full pathnames... if you really must. + + Version: MoreFiles + + Copyright: © 1995-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "FSpCompat.h" +#include "FullPath.h" + +/* + IMPORTANT NOTE: + + The use of full pathnames is strongly discouraged. Full pathnames are + particularly unreliable as a means of identifying files, directories + or volumes within your application, for two primary reasons: + + ¥ The user can change the name of any element in the path at virtually + any time. + ¥ Volume names on the Macintosh are *not* unique. Multiple + mounted volumes can have the same name. For this reason, the use of + a full pathname to identify a specific volume may not produce the + results you expect. If more than one volume has the same name and + a full pathname is used, the File Manager currently uses the first + mounted volume it finds with a matching name in the volume queue. + + In general, you should use a fileÕs name, parent directory ID, and + volume reference number to identify a file you want to open, delete, + or otherwise manipulate. + + If you need to remember the location of a particular file across + subsequent system boots, use the Alias Manager to create an alias record + describing the file. If the Alias Manager is not available, you can save + the fileÕs name, its parent directory ID, and the name of the volume on + which itÕs located. Although none of these methods is foolproof, they are + much more reliable than using full pathnames to identify files. + + Nonetheless, it is sometimes useful to display a fileÕs full pathname to + the user. For example, a backup utility might display a list of full + pathnames of files as it copies them onto the backup medium. Or, a + utility might want to display a dialog box showing the full pathname of + a file when it needs the userÕs confirmation to delete the file. No + matter how unreliable full pathnames may be from a file-specification + viewpoint, users understand them more readily than volume reference + numbers or directory IDs. (Hint: Use the TruncString function from + TextUtils.h with truncMiddle as the truncWhere argument to shorten + full pathnames to a displayable length.) + + The following technique for constructing the full pathname of a file is + intended for display purposes only. Applications that depend on any + particular structure of a full pathname are likely to fail on alternate + foreign file systems or under future system software versions. +*/ + +/*****************************************************************************/ + +pascal OSErr GetFullPath(short vRefNum, + long dirID, + ConstStr255Param name, + short *fullPathLength, + Handle *fullPath) +{ + OSErr result; + FSSpec spec; + + *fullPathLength = 0; + *fullPath = NULL; + + result = FSMakeFSSpecCompat(vRefNum, dirID, name, &spec); + if ( (result == noErr) || (result == fnfErr) ) + { + result = FSpGetFullPath(&spec, fullPathLength, fullPath); + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFullPath(const FSSpec *spec, + short *fullPathLength, + Handle *fullPath) +{ + OSErr result; + OSErr realResult; + FSSpec tempSpec; + CInfoPBRec pb; + + *fullPathLength = 0; + *fullPath = NULL; + + + /* Default to noErr */ + realResult = result = noErr; + + /* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */ + if ( spec->name[0] == 0 ) + { + result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec); + } + else + { + /* Make a copy of the input FSSpec that can be modified */ + BlockMoveData(spec, &tempSpec, sizeof(FSSpec)); + } + + if ( result == noErr ) + { + if ( tempSpec.parID == fsRtParID ) + { + /* The object is a volume */ + + /* Add a colon to make it a full pathname */ + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + + /* We're done */ + result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); + } + else + { + /* The object isn't a volume */ + + /* Is the object a file or a directory? */ + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrDirID = tempSpec.parID; + pb.dirInfo.ioFDirIndex = 0; + result = PBGetCatInfoSync(&pb); + // Allow file/directory name at end of path to not exist. + realResult = result; + if ( (result == noErr) || (result == fnfErr) ) + { + /* if the object is a directory, append a colon so full pathname ends with colon */ + if ( (result == noErr) && (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + } + + /* Put the object name in first */ + result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]); + if ( result == noErr ) + { + /* Get the ancestor directory names */ + pb.dirInfo.ioNamePtr = tempSpec.name; + pb.dirInfo.ioVRefNum = tempSpec.vRefNum; + pb.dirInfo.ioDrParID = tempSpec.parID; + do /* loop until we have an error or find the root directory */ + { + pb.dirInfo.ioFDirIndex = -1; + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + result = PBGetCatInfoSync(&pb); + if ( result == noErr ) + { + /* Append colon to directory name */ + ++tempSpec.name[0]; + tempSpec.name[tempSpec.name[0]] = ':'; + + /* Add directory name to beginning of fullPath */ + (void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]); + result = MemError(); + } + } while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) ); + } + } + } + } + + if ( result == noErr ) + { + /* Return the length */ + *fullPathLength = GetHandleSize(*fullPath); + result = realResult; // return realResult in case it was fnfErr + } + else + { + /* Dispose of the handle and return NULL and zero length */ + if ( *fullPath != NULL ) + { + DisposeHandle(*fullPath); + } + *fullPath = NULL; + *fullPathLength = 0; + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpLocationFromFullPath(short fullPathLength, + const void *fullPath, + FSSpec *spec) +{ + AliasHandle alias; + OSErr result; + Boolean wasChanged; + Str32 nullString; + + /* Create a minimal alias from the full pathname */ + nullString[0] = 0; /* null string to indicate no zone or server name */ + result = NewAliasMinimalFromFullPath(fullPathLength, fullPath, nullString, nullString, &alias); + if ( result == noErr ) + { + /* Let the Alias Manager resolve the alias. */ + result = ResolveAlias(NULL, alias, spec, &wasChanged); + + /* work around Alias Mgr sloppy volume matching bug */ + if ( spec->vRefNum == 0 ) + { + /* invalidate wrong FSSpec */ + spec->parID = 0; + spec->name[0] = 0; + result = nsvErr; + } + DisposeHandle((Handle)alias); /* Free up memory used */ + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr LocationFromFullPath(short fullPathLength, + const void *fullPath, + short *vRefNum, + long *parID, + Str31 name) +{ + OSErr result; + FSSpec spec; + + result = FSpLocationFromFullPath(fullPathLength, fullPath, &spec); + if ( result == noErr ) + { + *vRefNum = spec.vRefNum; + *parID = spec.parID; + BlockMoveData(&spec.name[0], &name[0], spec.name[0] + 1); + } + return ( result ); +} + +/*****************************************************************************/ + diff --git a/src/osx/carbon/morefile/FullPath.h b/src/osx/carbon/morefile/FullPath.h new file mode 100644 index 0000000000..e1710c12bc --- /dev/null +++ b/src/osx/carbon/morefile/FullPath.h @@ -0,0 +1,311 @@ +/* + File: FullPath.h + + Contains: Routines for dealing with full pathnames... if you really must. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1995-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +/* + IMPORTANT NOTE: + + The use of full pathnames is strongly discouraged. Full pathnames are + particularly unreliable as a means of identifying files, directories + or volumes within your application, for two primary reasons: + + ¥ The user can change the name of any element in the path at + virtually any time. + ¥ Volume names on the Macintosh are *not* unique. Multiple + mounted volumes can have the same name. For this reason, the use of + a full pathname to identify a specific volume may not produce the + results you expect. If more than one volume has the same name and + a full pathname is used, the File Manager currently uses the first + mounted volume it finds with a matching name in the volume queue. + + In general, you should use a fileÕs name, parent directory ID, and + volume reference number to identify a file you want to open, delete, + or otherwise manipulate. + + If you need to remember the location of a particular file across + subsequent system boots, use the Alias Manager to create an alias + record describing the file. If the Alias Manager is not available, you + can save the fileÕs name, its parent directory ID, and the name of the + volume on which itÕs located. Although none of these methods is + foolproof, they are much more reliable than using full pathnames to + identify files. + + Nonetheless, it is sometimes useful to display a fileÕs full pathname + to the user. For example, a backup utility might display a list of full + pathnames of files as it copies them onto the backup medium. Or, a + utility might want to display a dialog box showing the full pathname of + a file when it needs the userÕs confirmation to delete the file. No + matter how unreliable full pathnames may be from a file-specification + viewpoint, users understand them more readily than volume reference + numbers or directory IDs. (Hint: Use the TruncString function from + TextUtils.h with truncMiddle as the truncWhere argument to shorten + full pathnames to a displayable length.) + + The following technique for constructing the full pathname of a file is + intended for display purposes only. Applications that depend on any + particular structure of a full pathname are likely to fail on alternate + foreign file systems or under future system software versions. +*/ + +#ifndef __FULLPATH__ +#define __FULLPATH__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetFullPath( + short vRefNum, + long dirID, + ConstStr255Param name, + short * fullPathLength, + Handle * fullPath); + + +/* + The GetFullPath function builds a full pathname to the specified + object. The full pathname is returned in the newly created handle + fullPath and the length of the full pathname is returned in + fullPathLength. Your program is responsible for disposing of the + fullPath handle. + + Note that a full pathname can be made to a file/directory that does not + yet exist if all directories up to that file/directory exist. In this case, + GetFullPath will return a fnfErr. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + fullPathLength output: The number of characters in the full pathname. + If the function fails to create a full + pathname, it sets fullPathLength to 0. + fullPath output: A handle to the newly created full pathname + buffer. If the function fails to create a + full pathname, it sets fullPath to NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File or directory does not exist (fullPath + and fullPathLength are still valid) + paramErr -50 No default volume + memFullErr -108 Not enough memory + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpGetFullPath +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetFullPath( + const FSSpec * spec, + short * fullPathLength, + Handle * fullPath); + + +/* + The GetFullPath function builds a full pathname to the specified + object. The full pathname is returned in the newly created handle + fullPath and the length of the full pathname is returned in + fullPathLength. Your program is responsible for disposing of the + fullPath handle. + + Note that a full pathname can be made to a file/directory that does not + yet exist if all directories up to that file/directory exist. In this case, + FSpGetFullPath will return a fnfErr. + + IMPORTANT: The definition of a FSSpec is a volume reference number (not a + drive number, working directory number, or 0), a parent directory ID (not 0), + and the name of a file or folder (not an empty name, a full pathname, or + a partial pathname containing one or more colon (:) characters). + FSpGetFullPath assumes it is getting a FSSpec that matches the rules. + If you have an FSSpec record that wasn't created by FSMakeFSSpec (or + FSMakeFSSpecCompat from FSpCompat in MoreFiles which correctly builds + FSSpecs), you should call GetFullPath instead of FSpGetFullPath. + + spec input: An FSSpec record specifying the object. + fullPathLength output: The number of characters in the full pathname. + If the function fails to create a full pathname, + it sets fullPathLength to 0. + fullPath output: A handle to the newly created full pathname + buffer. If the function fails to create a + full pathname, it sets fullPath to NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File or directory does not exist (fullPath + and fullPathLength are still valid) + paramErr -50 No default volume + memFullErr -108 Not enough memory + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: GetFullPath +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpLocationFromFullPath( + short fullPathLength, + const void * fullPath, + FSSpec * spec); + + +/* + The FSpLocationFromFullPath function returns a FSSpec to the object + specified by full pathname. This function requires the Alias Manager. + + fullPathLength input: The number of characters in the full pathname + of the target. + fullPath input: A pointer to a buffer that contains the full + pathname of the target. The full pathname + starts with the name of the volume, includes + all of the directory names in the path to the + target, and ends with the target name. + spec output: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 The volume is not mounted + fnfErr -43 Target not found, but volume and parent + directory found + paramErr -50 Parameter error + usrCanceledErr -128 The user canceled the operation + + __________ + + See also: LocationFromFullPath +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +LocationFromFullPath( + short fullPathLength, + const void * fullPath, + short * vRefNum, + long * parID, + Str31 name); + + +/* + The LocationFromFullPath function returns the volume reference number, + parent directory ID and name of the object specified by full pathname. + This function requires the Alias Manager. + + fullPathLength input: The number of characters in the full pathname + of the target. + fullPath input: A pointer to a buffer that contains the full + pathname of the target. The full pathname starts + with the name of the volume, includes all of + the directory names in the path to the target, + and ends with the target name. + vRefNum output: The volume reference number. + parID output: The parent directory ID of the specified object. + name output: The name of the specified object. + + Result Codes + noErr 0 No error + nsvErr -35 The volume is not mounted + fnfErr -43 Target not found, but volume and parent + directory found + paramErr -50 Parameter error + usrCanceledErr -128 The user canceled the operation + + __________ + + See also: FSpLocationFromFullPath +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __FULLPATH__ */ + diff --git a/src/osx/carbon/morefile/IterateDirectory.c b/src/osx/carbon/morefile/IterateDirectory.c new file mode 100644 index 0000000000..e748829ff5 --- /dev/null +++ b/src/osx/carbon/morefile/IterateDirectory.c @@ -0,0 +1,207 @@ +/* + File: IterateDirectory.c + + Contains: File Manager directory iterator routines. + + Version: MoreFiles + + Copyright: © 1995-2001 by Jim Luther and Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFilesExtras.h" +#include "IterateDirectory.h" + +/* +** Type definitions +*/ + +/* The IterateGlobals structure is used to minimize the amount of +** stack space used when recursively calling IterateDirectoryLevel +** and to hold global information that might be needed at any time. +*/ +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#endif +struct IterateGlobals +{ + IterateFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */ + CInfoPBRec cPB; /* the parameter block used for PBGetCatInfo calls */ + Str63 itemName; /* the name of the current item */ + OSErr result; /* temporary holder of results - saves 2 bytes of stack each level */ + Boolean quitFlag; /* set to true if filter wants to kill interation */ + unsigned short maxLevels; /* Maximum levels to iterate through */ + unsigned short currentLevel; /* The current level IterateLevel is on */ + void *yourDataPtr; /* A pointer to caller data the filter may need to access */ +}; +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#endif + +typedef struct IterateGlobals IterateGlobals; +typedef IterateGlobals *IterateGlobalsPtr; + +/*****************************************************************************/ + +/* Static Prototype */ + +static void IterateDirectoryLevel(long dirID, + IterateGlobals *theGlobals); + +/*****************************************************************************/ + +/* +** Functions +*/ + +static void IterateDirectoryLevel(long dirID, + IterateGlobals *theGlobals) +{ + if ( (theGlobals->maxLevels == 0) || /* if maxLevels is zero, we aren't checking levels */ + (theGlobals->currentLevel < theGlobals->maxLevels) ) /* if currentLevel < maxLevels, look at this level */ + { + short index = 1; + + ++theGlobals->currentLevel; /* go to next level */ + + do + { /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */ + + /* Get next source item at the current directory level */ + + theGlobals->cPB.dirInfo.ioFDirIndex = index; + theGlobals->cPB.dirInfo.ioDrDirID = dirID; + theGlobals->result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->cPB); + + if ( theGlobals->result == noErr ) + { + /* Call the IterateFilterProc */ + CallIterateFilterProc(theGlobals->iterateFilter, &theGlobals->cPB, &theGlobals->quitFlag, theGlobals->yourDataPtr); + + /* Is it a directory? */ + if ( (theGlobals->cPB.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* We have a directory */ + if ( !theGlobals->quitFlag ) + { + /* Dive again if the IterateFilterProc didn't say "quit" */ + IterateDirectoryLevel(theGlobals->cPB.dirInfo.ioDrDirID, theGlobals); + } + } + } + + ++index; /* prepare to get next item */ + } while ( (theGlobals->result == noErr) && (!theGlobals->quitFlag) ); /* time to fall back a level? */ + + if ( (theGlobals->result == fnfErr) || /* fnfErr is OK - it only means we hit the end of this level */ + (theGlobals->result == afpAccessDenied) ) /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */ + { + theGlobals->result = noErr; + } + + --theGlobals->currentLevel; /* return to previous level as we leave */ + } +} + +/*****************************************************************************/ + +pascal OSErr IterateDirectory(short vRefNum, + long dirID, + ConstStr255Param name, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void *yourDataPtr) +{ + IterateGlobals theGlobals; + OSErr result; + long theDirID; + short theVRefNum; + Boolean isDirectory; + + /* Make sure there is a IterateFilter */ + if ( iterateFilter != NULL ) + { + /* Get the real directory ID and make sure it is a directory */ + result = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory); + if ( result == noErr ) + { + if ( isDirectory == true ) + { + /* Get the real vRefNum */ + result = DetermineVRefNum(name, vRefNum, &theVRefNum); + if ( result == noErr ) + { + /* Set up the globals we need to access from the recursive routine. */ + theGlobals.iterateFilter = iterateFilter; + theGlobals.cPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName; + theGlobals.cPB.hFileInfo.ioVRefNum = theVRefNum; + theGlobals.itemName[0] = 0; + theGlobals.result = noErr; + theGlobals.quitFlag = false; + theGlobals.maxLevels = maxLevels; + theGlobals.currentLevel = 0; /* start at level 0 */ + theGlobals.yourDataPtr = yourDataPtr; + + /* Here we go into recursion land... */ + IterateDirectoryLevel(theDirID, &theGlobals); + + result = theGlobals.result; /* set the result */ + } + } + else + { + result = dirNFErr; /* a file was passed instead of a directory */ + } + } + } + else + { + result = paramErr; /* iterateFilter was NULL */ + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSpIterateDirectory(const FSSpec *spec, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void *yourDataPtr) +{ + return ( IterateDirectory(spec->vRefNum, spec->parID, spec->name, + maxLevels, iterateFilter, yourDataPtr) ); +} + +/*****************************************************************************/ diff --git a/src/osx/carbon/morefile/IterateDirectory.h b/src/osx/carbon/morefile/IterateDirectory.h new file mode 100644 index 0000000000..ef6b113eea --- /dev/null +++ b/src/osx/carbon/morefile/IterateDirectory.h @@ -0,0 +1,222 @@ +/* + File: IterateDirectory.h + + Contains: File Manager directory iterator routines. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1995-2001 by Jim Luther and Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __ITERATEDIRECTORY__ +#define __ITERATEDIRECTORY__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +typedef CALLBACK_API( void , IterateFilterProcPtr )(const CInfoPBRec *cpbPtr, Boolean *quitFlag, void *yourDataPtr); +/* + This is the prototype for the IterateFilterProc function which is + called once for each file and directory found by IterateDirectory. The + IterateFilterProc gets a pointer to the CInfoPBRec that IterateDirectory + used to call PBGetCatInfo. The IterateFilterProc can use the read-only + data in the CInfoPBRec for whatever it wants. + + If the IterateFilterProc wants to stop IterateDirectory, it can set + quitFlag to true (quitFlag will be passed to the IterateFilterProc + false). + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateFilterProc. + + cpbPtr input: A pointer to the CInfoPBRec that IterateDirectory + used to call PBGetCatInfo. The CInfoPBRec and the + data it points to must not be changed by your + IterateFilterProc. + quitFlag output: Your IterateFilterProc can set quitFlag to true + if it wants to stop IterateDirectory. + yourDataPtr input: A pointer to whatever data structure you might + want to access from within the IterateFilterProc. + + __________ + + Also see: IterateDirectory, FSpIterateDirectory +*/ +#define CallIterateFilterProc(userRoutine, cpbPtr, quitFlag, yourDataPtr) \ + (*(userRoutine))((cpbPtr), (quitFlag), (yourDataPtr)) + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +IterateDirectory( + short vRefNum, + long dirID, + ConstStr255Param name, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void * yourDataPtr); + + +/* + The IterateDirectory function performs a recursive iteration (scan) of + the specified directory and calls your IterateFilterProc function once + for each file and directory found. + + The maxLevels parameter lets you control how deep the recursion goes. + If maxLevels is 1, IterateDirectory only scans the specified directory; + if maxLevels is 2, IterateDirectory scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateFilterProc. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + maxLevels input: Maximum number of directory levels to scan or + zero to scan all directory levels. + iterateFilter input: A pointer to the routine you want called once + for each file and directory found by + IterateDirectory. + yourDataPtr input: A pointer to whatever data structure you might + want to access from within the IterateFilterProc. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume or iterateFilter was NULL + dirNFErr -120 Directory not found or incomplete pathname + or a file was passed instead of a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: IterateFilterProcPtr, FSpIterateDirectory +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpIterateDirectory( + const FSSpec * spec, + unsigned short maxLevels, + IterateFilterProcPtr iterateFilter, + void * yourDataPtr); + + +/* + The FSpIterateDirectory function performs a recursive iteration (scan) + of the specified directory and calls your IterateFilterProc function once + for each file and directory found. + + The maxLevels parameter lets you control how deep the recursion goes. + If maxLevels is 1, FSpIterateDirectory only scans the specified directory; + if maxLevels is 2, FSpIterateDirectory scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateFilterProc. + + spec input: An FSSpec record specifying the directory to scan. + maxLevels input: Maximum number of directory levels to scan or + zero to scan all directory levels. + iterateFilter input: A pointer to the routine you want called once + for each file and directory found by + FSpIterateDirectory. + yourDataPtr input: A pointer to whatever data structure you might + want to access from within the IterateFilterProc. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume or iterateFilter was NULL + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: IterateFilterProcPtr, IterateDirectory +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __ITERATEDIRECTORY__ */ + diff --git a/src/osx/carbon/morefile/MoreDesktopMgr.c b/src/osx/carbon/morefile/MoreDesktopMgr.c new file mode 100644 index 0000000000..880ed263f0 --- /dev/null +++ b/src/osx/carbon/morefile/MoreDesktopMgr.c @@ -0,0 +1,1270 @@ +/* + File: MoreDesktopMgr.c + + Contains: A collection of useful high-level Desktop Manager routines. + If the Desktop Manager is not available, use the Desktop file + for 'read' operations. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + (NG) Nitin Ganatra + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. Updated + various routines to use new calling convention of the + MoreFilesExtras accessor functions. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFiles.h" +#include "MoreFilesExtras.h" +#include "Search.h" +#include "MoreDesktopMgr.h" + +/*****************************************************************************/ + +/* Desktop file notes: +** +** ¥ The Desktop file is owned by the Finder and is normally open by the +** Finder. That means that we only have read-only access to the Desktop +** file. +** ¥ Since the Resource Manager doesn't support shared access to resource +** files and we're using read-only access, we don't ever leave the +** Desktop file open. We open a path to it, get the data we want out +** of it, and then close the open path. This is the only safe way to +** open a resource file with read-only access since some other program +** could have it open with write access. +** ¥ The bundle related resources in the Desktop file are normally +** purgable, so when we're looking through them, we don't bother to +** release resources we're done looking at - closing the resource file +** (which we always do) will release them. +** ¥ Since we can't assume the Desktop file is named "Desktop" +** (it probably is everywhere but France), we get the Desktop +** file's name by searching the volume's root directory for a file +** with fileType == 'FNDR' and creator == 'ERIK'. The only problem with +** this scheme is that someone could create another file with that type +** and creator in the root directory and we'd find the wrong file. +** The chances of this are very slim. +*/ + +/*****************************************************************************/ + +/* local defines */ + +enum +{ + kBNDLResType = 'BNDL', + kFREFResType = 'FREF', + kIconFamResType = 'ICN#', + kFCMTResType = 'FCMT', + kAPPLResType = 'APPL' +}; + +/*****************************************************************************/ + +/* local data structures */ + +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#endif + +struct IDRec +{ + short localID; + short rsrcID; +}; +typedef struct IDRec IDRec; +typedef IDRec *IDRecPtr; + +struct BundleType +{ + OSType type; /* 'ICN#' or 'FREF' */ + short count; /* number of IDRecs - 1 */ + IDRec idArray[1]; +}; +typedef struct BundleType BundleType; +typedef BundleType *BundleTypePtr; + +struct BNDLRec +{ + OSType signature; /* creator type signature */ + short versionID; /* version - should always be 0 */ + short numTypes; /* number of elements in typeArray - 1 */ + BundleType typeArray[1]; +}; +typedef struct BNDLRec BNDLRec; +typedef BNDLRec **BNDLRecHandle; + +struct FREFRec +{ + OSType fileType; /* file type */ + short iconID; /* icon local ID */ + Str255 fileName; /* file name */ +}; +typedef struct FREFRec FREFRec; +typedef FREFRec **FREFRecHandle; + +struct APPLRec +{ + OSType creator; /* creator type signature */ + long parID; /* parent directory ID */ + Str255 applName; /* application name */ +}; +typedef struct APPLRec APPLRec; +typedef APPLRec *APPLRecPtr; + +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#endif + +/*****************************************************************************/ + +/* static prototypes */ + +static OSErr GetDesktopFileName(short vRefNum, + Str255 desktopName); + +static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName, + short vRefNum, + OSType creator, + short *applVRefNum, + long *applParID, + Str255 applName); + +static OSErr FindBundleGivenCreator(OSType creator, + BNDLRecHandle *returnBndl); + +static OSErr FindTypeInBundle(OSType typeToFind, + BNDLRecHandle theBndl, + BundleTypePtr *returnBundleType); + +static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType, + OSType fileType, + short *iconLocalID); + +static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType, + short iconLocalID, + short *iconRsrcID); + +static OSType DTIconToResIcon(short iconType); + +static OSErr GetIconFromDesktopFile(ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle *iconHandle); + +static OSErr GetCommentID(short vRefNum, + long dirID, + ConstStr255Param name, + short *commentID); + +static OSErr GetCommentFromDesktopFile(short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment); + +/*****************************************************************************/ + +/* +** GetDesktopFileName +** +** Get the name of the Desktop file. +*/ +static OSErr GetDesktopFileName(short vRefNum, + Str255 desktopName) +{ + OSErr error; + HParamBlockRec pb; + short index; + Boolean found; + + pb.fileParam.ioNamePtr = desktopName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioFVersNum = 0; + index = 1; + found = false; + do + { + pb.fileParam.ioDirID = fsRtDirID; + pb.fileParam.ioFDirIndex = index; + error = PBHGetFInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.fileParam.ioFlFndrInfo.fdType == 'FNDR') && + (pb.fileParam.ioFlFndrInfo.fdCreator == 'ERIK') ) + { + found = true; + } + } + ++index; + } while ( (error == noErr) && !found ); + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTOpen(ConstStr255Param volName, + short vRefNum, + short *dtRefNum, + Boolean *newDTDatabase) +{ + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize; + DTPBRec pb; + + /* Check for volume Desktop Manager support before calling */ + infoSize = sizeof(GetVolParmsInfoBuffer); + error = HGetVolParms(volName, vRefNum, &volParmsInfo, &infoSize); + if ( error == noErr ) + { + if ( hasDesktopMgr(&volParmsInfo) ) + { + pb.ioNamePtr = (StringPtr)volName; + pb.ioVRefNum = vRefNum; + error = PBDTOpenInform(&pb); + /* PBDTOpenInform informs us if the desktop was just created */ + /* by leaving the low bit of ioTagInfo clear (0) */ + *newDTDatabase = ((pb.ioTagInfo & 1L) == 0); + if ( error == paramErr ) + { + error = PBDTGetPath(&pb); + /* PBDTGetPath doesn't tell us if the database is new */ + /* so assume it is not new */ + *newDTDatabase = false; + } + *dtRefNum = pb.ioDTRefNum; + } + else + { + error = paramErr; + } + } + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetAPPLFromDesktopFile +** +** Get a application's location from the +** Desktop file's 'APPL' resources. +*/ +static OSErr GetAPPLFromDesktopFile(ConstStr255Param volName, + short vRefNum, + OSType creator, + short *applVRefNum, + long *applParID, + Str255 applName) +{ + OSErr error; + short realVRefNum; + Str255 desktopName; + short savedResFile; + short dfRefNum; + Handle applResHandle; + Boolean foundCreator; + Ptr applPtr; + long applSize; + + error = DetermineVRefNum(volName, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = GetDesktopFileName(realVRefNum, desktopName); + if ( error == noErr ) + { + savedResFile = CurResFile(); + /* + ** Open the 'Desktop' file in the root directory. (because + ** opening the resource file could preload unwanted resources, + ** bracket the call with SetResLoad(s)) + */ + SetResLoad(false); + dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm); + SetResLoad(true); + + if ( dfRefNum != -1) + { + /* Get 'APPL' resource ID 0 */ + applResHandle = Get1Resource(kAPPLResType, 0); + if ( applResHandle != NULL ) + { + applSize = GetHandleSize((Handle)applResHandle); + if ( applSize != 0 ) /* make sure the APPL resource isn't empty */ + { + foundCreator = false; + applPtr = *applResHandle; + + /* APPL's don't have a count so I have to use the size as the bounds */ + while ( (foundCreator == false) && + (applPtr < (*applResHandle + applSize)) ) + { + if ( ((APPLRecPtr)applPtr)->creator == creator ) + { + foundCreator = true; + } + else + { + /* fun with pointer math... */ + applPtr += sizeof(OSType) + + sizeof(long) + + ((APPLRecPtr)applPtr)->applName[0] + 1; + /* application mappings are word aligned within the resource */ + if ( ((unsigned long)applPtr % 2) != 0 ) + { + applPtr += 1; + } + } + } + if ( foundCreator == true ) + { + *applVRefNum = realVRefNum; + *applParID = ((APPLRecPtr)applPtr)->parID; + BlockMoveData(((APPLRecPtr)applPtr)->applName, + applName, + ((APPLRecPtr)applPtr)->applName[0] + 1); + /* error is already noErr */ + } + else + { + error = afpItemNotFound; /* didn't find a creator match */ + } + } + else + { + error = afpItemNotFound; /* no APPL mapping available */ + } + } + else + { + error = afpItemNotFound; /* no APPL mapping available */ + } + + /* restore the resource chain and close the Desktop file */ + UseResFile(savedResFile); + CloseResFile(dfRefNum); + } + else + { + error = afpItemNotFound; + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTXGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + short *applVRefNum, + long *applParID, + Str255 applName) +{ + OSErr error; + UniversalFMPB pb; + short dtRefNum; + Boolean newDTDatabase; + short realVRefNum; + short index; + Boolean applFound; + FSSpec spec; + long actMatchCount; + + /* get the real vRefNum */ + error = DetermineVRefNum(volName, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + if ( !newDTDatabase ) + { + index = 0; + applFound = false; + do + { + pb.dtPB.ioNamePtr = applName; + pb.dtPB.ioDTRefNum = dtRefNum; + pb.dtPB.ioIndex = index; + pb.dtPB.ioFileCreator = creator; + error = PBDTGetAPPLSync(&pb.dtPB); + if ( error == noErr ) + { + /* got a match - see if it is valid */ + + *applVRefNum = realVRefNum; /* get the vRefNum now */ + *applParID = pb.dtPB.ioAPPLParID; /* get the parent ID now */ + + /* pb.hPB.fileParam.ioNamePtr is already set */ + pb.hPB.fileParam.ioVRefNum = realVRefNum; + pb.hPB.fileParam.ioFVersNum = 0; + pb.hPB.fileParam.ioDirID = *applParID; + pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + if ( PBHGetFInfoSync(&pb.hPB) == noErr ) + { + if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator == creator) && + (pb.hPB.fileParam.ioFlFndrInfo.fdType == 'APPL') ) + { + applFound = true; + } + } + } + ++index; + } while ( (error == noErr) && !applFound ); + if ( error != noErr ) + { + error = afpItemNotFound; + } + } + else + { + /* Desktop database is empty (new), set error to try CatSearch */ + error = afpItemNotFound; + } + } + /* acceptable errors from Desktop Manager to continue are paramErr or afpItemNotFound */ + if ( error == paramErr ) + { + /* if paramErr, the volume didn't support the Desktop Manager */ + /* try the Desktop file */ + + error = GetAPPLFromDesktopFile(volName, vRefNum, creator, + applVRefNum, applParID, applName); + if ( error == noErr ) + { + /* got a match - see if it is valid */ + + pb.hPB.fileParam.ioNamePtr = applName; + pb.hPB.fileParam.ioVRefNum = *applVRefNum; + pb.hPB.fileParam.ioFVersNum = 0; + pb.hPB.fileParam.ioDirID = *applParID; + pb.hPB.fileParam.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + if ( PBHGetFInfoSync(&pb.hPB) == noErr ) + { + if ( (pb.hPB.fileParam.ioFlFndrInfo.fdCreator != creator) || + (pb.hPB.fileParam.ioFlFndrInfo.fdType != 'APPL') ) + { + error = afpItemNotFound; + } + } + else if ( error == fnfErr ) + { + error = afpItemNotFound; + } + } + } + /* acceptable error from DesktopFile code to continue is afpItemNotFound */ + if ( (error == afpItemNotFound) && searchCatalog) + { + /* Couldn't be found in the Desktop file either, */ + /* try searching with CatSearch if requested */ + + error = CreatorTypeFileSearch(NULL, realVRefNum, creator, kAPPLResType, &spec, 1, + &actMatchCount, true); + if ( (error == noErr) || (error == eofErr) ) + { + if ( actMatchCount > 0 ) + { + *applVRefNum = spec.vRefNum; + *applParID = spec.parID; + BlockMoveData(spec.name, applName, spec.name[0] + 1); + } + else + { + error = afpItemNotFound; + } + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTXGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + FSSpec *spec) +{ + return ( DTXGetAPPL(volName, vRefNum, creator, searchCatalog, + &(spec->vRefNum), &(spec->parID), spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr DTGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + short *applVRefNum, + long *applParID, + Str255 applName) +{ + /* Call DTXGetAPPL with the "searchCatalog" parameter true */ + return ( DTXGetAPPL(volName, vRefNum, creator, true, + applVRefNum, applParID, applName) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTGetAPPL(ConstStr255Param volName, + short vRefNum, + OSType creator, + FSSpec *spec) +{ + /* Call DTXGetAPPL with the "searchCatalog" parameter true */ + return ( DTXGetAPPL(volName, vRefNum, creator, true, + &(spec->vRefNum), &(spec->parID), spec->name) ); +} + +/*****************************************************************************/ + +/* +** FindBundleGivenCreator +** +** Search the current resource file for the 'BNDL' resource with the given +** creator and return a handle to it. +*/ +static OSErr FindBundleGivenCreator(OSType creator, + BNDLRecHandle *returnBndl) +{ + OSErr error; + short numOfBundles; + short index; + BNDLRecHandle theBndl; + + error = afpItemNotFound; /* default to not found */ + + /* Search each BNDL resource until we find the one with a matching creator. */ + + numOfBundles = Count1Resources(kBNDLResType); + index = 1; + *returnBndl = NULL; + + while ( (index <= numOfBundles) && (*returnBndl == NULL) ) + { + theBndl = (BNDLRecHandle)Get1IndResource(kBNDLResType, index); + + if ( theBndl != NULL ) + { + if ( (*theBndl)->signature == creator ) + { + /* numTypes and typeArray->count will always be the actual count minus 1, */ + /* so 0 in both fields is valid. */ + if ( ((*theBndl)->numTypes >= 0) && ((*theBndl)->typeArray->count >= 0) ) + { + /* got it */ + *returnBndl = theBndl; + error = noErr; + } + } + } + + index ++; + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** FindTypeInBundle +** +** Given a Handle to a BNDL return a pointer to the desired type +** in it. If the type is not found, or if the type's count < 0, +** return afpItemNotFound. +*/ +static OSErr FindTypeInBundle(OSType typeToFind, + BNDLRecHandle theBndl, + BundleTypePtr *returnBundleType) +{ + OSErr error; + short index; + Ptr ptrIterator; /* use a Ptr so we can do ugly pointer math */ + + error = afpItemNotFound; /* default to not found */ + + ptrIterator = (Ptr)((*theBndl)->typeArray); + index = 0; + *returnBundleType = NULL; + + while ( (index < ((*theBndl)->numTypes + 1)) && + (*returnBundleType == NULL) ) + { + if ( (((BundleTypePtr)ptrIterator)->type == typeToFind) && + (((BundleTypePtr)ptrIterator)->count >= 0) ) + { + *returnBundleType = (BundleTypePtr)ptrIterator; + error = noErr; + } + else + { + ptrIterator += ( sizeof(OSType) + + sizeof(short) + + ( sizeof(IDRec) * (((BundleTypePtr)ptrIterator)->count + 1) ) ); + ++index; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetLocalIDFromFREF +** +** Given a pointer to a 'FREF' BundleType record, load each 'FREF' resource +** looking for a matching fileType. If a matching fileType is found, return +** its icon local ID. If no match is found, return afpItemNotFound as the +** function result. +*/ +static OSErr GetLocalIDFromFREF(BundleTypePtr theBundleType, + OSType fileType, + short *iconLocalID) +{ + OSErr error; + short index; + IDRecPtr idIterator; + FREFRecHandle theFref; + + error = afpItemNotFound; /* default to not found */ + + /* For each localID in this type, get the FREF resource looking for fileType */ + index = 0; + idIterator = &theBundleType->idArray[0]; + *iconLocalID = 0; + + while ( (index <= theBundleType->count) && (*iconLocalID == 0) ) + { + theFref = (FREFRecHandle)Get1Resource(kFREFResType, idIterator->rsrcID); + if ( theFref != NULL ) + { + if ( (*theFref)->fileType == fileType ) + { + *iconLocalID = (*theFref)->iconID; + error = noErr; + } + } + + ++idIterator; + ++index; + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetIconRsrcIDFromLocalID +** +** Given a pointer to a 'ICN#' BundleType record, look for the IDRec with +** the localID that matches iconLocalID. If a matching IDRec is found, +** return the IDRec's rsrcID field value. If no match is found, return +** afpItemNotFound as the function result. +*/ +static OSErr GetIconRsrcIDFromLocalID(BundleTypePtr theBundleType, + short iconLocalID, + short *iconRsrcID) +{ + OSErr error; + short index; + IDRecPtr idIterator; + + error = afpItemNotFound; /* default to not found */ + + /* Find the rsrcID of the icon family type, given the localID */ + index = 0; + idIterator = &theBundleType->idArray[0]; + *iconRsrcID = 0; + + while ( (index <= theBundleType->count) && (*iconRsrcID == 0) ) + { + if ( idIterator->localID == iconLocalID ) + { + *iconRsrcID = idIterator->rsrcID; + error = noErr; + } + + idIterator ++; + index ++; + } + + return ( error ); +} + +/*****************************************************************************/ + +/* +** DTIconToResIcon +** +** Map a Desktop Manager icon type to the corresponding resource type. +** Return (OSType)0 if there is no corresponding resource type. +*/ +static OSType DTIconToResIcon(short iconType) +{ + OSType resType; + + switch ( iconType ) + { + case kLargeIcon: + resType = large1BitMask; + break; + case kLarge4BitIcon: + resType = large4BitData; + break; + case kLarge8BitIcon: + resType = large8BitData; + break; + case kSmallIcon: + resType = small1BitMask; + break; + case kSmall4BitIcon: + resType = small4BitData; + break; + case kSmall8BitIcon: + resType = small8BitData; + break; + default: + resType = (OSType)0; + break; + } + + return ( resType ); +} + +/*****************************************************************************/ + +/* +** GetIconFromDesktopFile +** +** INPUT a pointer to a non-existent Handle, because we'll allocate one +** +** search each BNDL resource for the right fileCreator and once we get it +** find the 'FREF' type in BNDL +** for each localID in the type, open the FREF resource +** if the FREF is the desired fileType +** get its icon localID +** get the ICN# type in BNDL +** get the icon resource number from the icon localID +** get the icon resource type from the desktop mgr's iconType +** get the icon of that type and number +*/ +static OSErr GetIconFromDesktopFile(ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle *iconHandle) +{ + OSErr error; + short realVRefNum; + Str255 desktopName; + short savedResFile; + short dfRefNum; + BNDLRecHandle theBndl = NULL; + BundleTypePtr theBundleType; + short iconLocalID; + short iconRsrcID; + OSType iconRsrcType; + Handle returnIconHandle; + char bndlState; + + *iconHandle = NULL; + + error = DetermineVRefNum(volName, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = GetDesktopFileName(realVRefNum, desktopName); + if ( error == noErr ) + { + savedResFile = CurResFile(); + + /* + ** Open the 'Desktop' file in the root directory. (because + ** opening the resource file could preload unwanted resources, + ** bracket the call with SetResLoad(s)) + */ + SetResLoad(false); + dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm); + SetResLoad(true); + + if ( dfRefNum != -1 ) + { + /* + ** Find the BNDL resource with the specified creator. + */ + error = FindBundleGivenCreator(fileCreator, &theBndl); + if ( error == noErr ) + { + /* Lock the BNDL resource so it won't be purged when other resources are loaded */ + bndlState = HGetState((Handle)theBndl); + HLock((Handle)theBndl); + + /* Find the 'FREF' BundleType record in the BNDL resource. */ + error = FindTypeInBundle(kFREFResType, theBndl, &theBundleType); + if ( error == noErr ) + { + /* Find the local ID in the 'FREF' resource with the specified fileType */ + error = GetLocalIDFromFREF(theBundleType, fileType, &iconLocalID); + if ( error == noErr ) + { + /* Find the 'ICN#' BundleType record in the BNDL resource. */ + error = FindTypeInBundle(kIconFamResType, theBndl, &theBundleType); + if ( error == noErr ) + { + /* Find the icon's resource ID in the 'ICN#' BundleType record */ + error = GetIconRsrcIDFromLocalID(theBundleType, iconLocalID, &iconRsrcID); + if ( error == noErr ) + { + /* Map Desktop Manager icon type to resource type */ + iconRsrcType = DTIconToResIcon(iconType); + + if ( iconRsrcType != (OSType)0 ) + { + /* Load the icon */ + returnIconHandle = Get1Resource(iconRsrcType, iconRsrcID); + if ( returnIconHandle != NULL ) + { + /* Copy the resource handle, and return the copy */ + HandToHand(&returnIconHandle); + if ( MemError() == noErr ) + { + *iconHandle = returnIconHandle; + } + else + { + error = afpItemNotFound; + } + } + else + { + error = afpItemNotFound; + } + } + } + } + } + } + /* Restore the state of the BNDL resource */ + HSetState((Handle)theBndl, bndlState); + } + /* Restore the resource chain and close the Desktop file */ + UseResFile(savedResFile); + CloseResFile(dfRefNum); + } + else + { + error = ResError(); /* could not open Desktop file */ + } + } + if ( (error != noErr) && (error != memFullErr) ) + { + error = afpItemNotFound; /* force an error we should return */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTGetIcon(ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle *iconHandle) +{ + OSErr error; + DTPBRec pb; + short dtRefNum; + Boolean newDTDatabase; + Size bufferSize; + + *iconHandle = NULL; + error = DTOpen(volName, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + /* there was a desktop database and it's now open */ + + if ( !newDTDatabase ) /* don't bother to look in a new (empty) database */ + { + /* get the buffer size for the requested icon type */ + switch ( iconType ) + { + case kLargeIcon: + bufferSize = kLargeIconSize; + break; + case kLarge4BitIcon: + bufferSize = kLarge4BitIconSize; + break; + case kLarge8BitIcon: + bufferSize = kLarge8BitIconSize; + break; + case kSmallIcon: + bufferSize = kSmallIconSize; + break; + case kSmall4BitIcon: + bufferSize = kSmall4BitIconSize; + break; + case kSmall8BitIcon: + bufferSize = kSmall8BitIconSize; + break; + default: + iconType = 0; + bufferSize = 0; + break; + } + if ( bufferSize != 0 ) + { + *iconHandle = NewHandle(bufferSize); + if ( *iconHandle != NULL ) + { + HLock(*iconHandle); + + pb.ioDTRefNum = dtRefNum; + pb.ioTagInfo = 0; + pb.ioDTBuffer = **iconHandle; + pb.ioDTReqCount = bufferSize; + pb.ioIconType = iconType; + pb.ioFileCreator = fileCreator; + pb.ioFileType = fileType; + error = PBDTGetIconSync(&pb); + + HUnlock(*iconHandle); + + if ( error != noErr ) + { + DisposeHandle(*iconHandle); /* dispose of the allocated memory */ + *iconHandle = NULL; + } + } + else + { + error = memFullErr; /* handle could not be allocated */ + } + } + else + { + error = paramErr; /* unknown icon type requested */ + } + } + else + { + error = afpItemNotFound; /* the desktop database was empty - nothing to return */ + } + } + else + { + /* There is no desktop database - try the Desktop file */ + + error = GetIconFromDesktopFile(volName, vRefNum, iconType, + fileCreator, fileType, iconHandle); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTSetComment(short vRefNum, + long dirID, + ConstStr255Param name, + ConstStr255Param comment) +{ + DTPBRec pb; + OSErr error; + short dtRefNum; + Boolean newDTDatabase; + + error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + pb.ioDTRefNum = dtRefNum; + pb.ioNamePtr = (StringPtr)name; + pb.ioDirID = dirID; + pb.ioDTBuffer = (Ptr)&comment[1]; + /* Truncate the comment to 200 characters just in case */ + /* some file system doesn't range check */ + if ( comment[0] <= 200 ) + { + pb.ioDTReqCount = comment[0]; + } + else + { + pb.ioDTReqCount = 200; + } + error = PBDTSetCommentSync(&pb); + } + return (error); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTSetComment(const FSSpec *spec, + ConstStr255Param comment) +{ + return (DTSetComment(spec->vRefNum, spec->parID, spec->name, comment)); +} + +/*****************************************************************************/ + +/* +** GetCommentID +** +** Get the comment ID number for the Desktop file's 'FCMT' resource ID from +** the file or folders fdComment (frComment) field. +*/ +static OSErr GetCommentID(short vRefNum, + long dirID, + ConstStr255Param name, + short *commentID) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + *commentID = pb.hFileInfo.ioFlXFndrInfo.fdComment; + return ( error ); +} + +/*****************************************************************************/ + +/* +** GetCommentFromDesktopFile +** +** Get a file or directory's Finder comment field (if any) from the +** Desktop file's 'FCMT' resources. +*/ +static OSErr GetCommentFromDesktopFile(short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment) +{ + OSErr error; + short commentID; + short realVRefNum; + Str255 desktopName; + short savedResFile; + short dfRefNum; + StringHandle commentHandle; + + /* Get the comment ID number */ + error = GetCommentID(vRefNum, dirID, name, &commentID); + if ( error == noErr ) + { + if ( commentID != 0 ) /* commentID == 0 means there's no comment */ + { + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = GetDesktopFileName(realVRefNum, desktopName); + if ( error == noErr ) + { + savedResFile = CurResFile(); + /* + ** Open the 'Desktop' file in the root directory. (because + ** opening the resource file could preload unwanted resources, + ** bracket the call with SetResLoad(s)) + */ + SetResLoad(false); + dfRefNum = HOpenResFile(realVRefNum, fsRtDirID, desktopName, fsRdPerm); + SetResLoad(true); + + if ( dfRefNum != -1) + { + /* Get the comment resource */ + commentHandle = (StringHandle)Get1Resource(kFCMTResType,commentID); + if ( commentHandle != NULL ) + { + if ( GetHandleSize((Handle)commentHandle) > 0 ) + { + BlockMoveData(*commentHandle, comment, *commentHandle[0] + 1); + } + else + { + error = afpItemNotFound; /* no comment available */ + } + } + else + { + error = afpItemNotFound; /* no comment available */ + } + + /* restore the resource chain and close the Desktop file */ + UseResFile(savedResFile); + CloseResFile(dfRefNum); + } + else + { + error = afpItemNotFound; + } + } + else + { + error = afpItemNotFound; + } + } + } + else + { + error = afpItemNotFound; /* no comment available */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DTGetComment(short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment) +{ + DTPBRec pb; + OSErr error; + short dtRefNum; + Boolean newDTDatabase; + + if (comment != NULL) + { + comment[0] = 0; /* return nothing by default */ + + /* attempt to open the desktop database */ + error = DTOpen(name, vRefNum, &dtRefNum, &newDTDatabase); + if ( error == noErr ) + { + /* There was a desktop database and it's now open */ + + if ( !newDTDatabase ) + { + pb.ioDTRefNum = dtRefNum; + pb.ioNamePtr = (StringPtr)name; + pb.ioDirID = dirID; + pb.ioDTBuffer = (Ptr)&comment[1]; + /* + ** IMPORTANT NOTE #1: Inside Macintosh says that comments + ** are up to 200 characters. While that may be correct for + ** the HFS file system's Desktop Manager, other file + ** systems (such as Apple Photo Access) return up to + ** 255 characters. Make sure the comment buffer is a Str255 + ** or you'll regret it. + ** + ** IMPORTANT NOTE #2: Although Inside Macintosh doesn't + ** mention it, ioDTReqCount is a input field to + ** PBDTGetCommentSync. Some file systems (like HFS) ignore + ** ioDTReqCount and always return the full comment -- + ** others (like AppleShare) respect ioDTReqCount and only + ** return up to ioDTReqCount characters of the comment. + */ + pb.ioDTReqCount = sizeof(Str255) - 1; + error = PBDTGetCommentSync(&pb); + if (error == noErr) + { + comment[0] = (unsigned char)pb.ioDTActCount; + } + } + } + else + { + /* There is no desktop database - try the Desktop file */ + error = GetCommentFromDesktopFile(vRefNum, dirID, name, comment); + if ( error != noErr ) + { + error = afpItemNotFound; /* return an expected error */ + } + } + } + else + { + error = paramErr; + } + + return (error); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTGetComment(const FSSpec *spec, + Str255 comment) +{ + return (DTGetComment(spec->vRefNum, spec->parID, spec->name, comment)); +} + +/*****************************************************************************/ + +pascal OSErr DTCopyComment(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName) +/* The destination volume must support the Desktop Manager for this to work */ +{ + OSErr error; + Str255 comment; + + error = DTGetComment(srcVRefNum, srcDirID, srcName, comment); + if ( (error == noErr) && (comment[0] > 0) ) + { + error = DTSetComment(dstVRefNum, dstDirID, dstName, comment); + } + return (error); +} + +/*****************************************************************************/ + +pascal OSErr FSpDTCopyComment(const FSSpec *srcSpec, + const FSSpec *dstSpec) +/* The destination volume must support the Desktop Manager for this to work */ +{ + return (DTCopyComment(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name)); +} + +/*****************************************************************************/ diff --git a/src/osx/carbon/morefile/MoreDesktopMgr.h b/src/osx/carbon/morefile/MoreDesktopMgr.h new file mode 100644 index 0000000000..110c673403 --- /dev/null +++ b/src/osx/carbon/morefile/MoreDesktopMgr.h @@ -0,0 +1,628 @@ +/* + File: MoreDesktopMgr.h + + Contains: A collection of useful high-level Desktop Manager routines. If the Desktop Manager is not available, use the Desktop file for 'read' operations. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __MOREDESKTOPMGR__ +#define __MOREDESKTOPMGR__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DTOpen( + ConstStr255Param volName, + short vRefNum, + short * dtRefNum, + Boolean * newDTDatabase); + + +/* + The DTOpen function opens a volume's desktop database. It returns + the reference number of the desktop database and indicates if the + desktop database was created as a result of this call (if it was created, + then it is empty). + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + dtRefNum output: The reference number of Desktop Manager's + desktop database on the specified volume. + newDTDatabase output: true if the desktop database was created as a + result of this call and thus empty. + false if the desktop database was already created, + or if it could not be determined if it was already + created. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DTXGetAPPL( + ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + short * applVRefNum, + long * applParID, + Str255 applName); + + +/* + The DTXGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails and searchCatalog is true, then it tries to find an application + with the specified creator using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + searchCatalog input: If true, search the catalog for the application + if it isn't found in the desktop database. + applVRefNum output: The volume reference number of the volume the + application is on. + applParID output: The parent directory ID of the application. + applName output: The name of the application. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTGetAPPL +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDTXGetAPPL( + ConstStr255Param volName, + short vRefNum, + OSType creator, + Boolean searchCatalog, + FSSpec * spec); + + +/* + The FSpDTXGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails and searchCatalog is true, then it tries to find an application + with the specified creator using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + searchCatalog input: If true, search the catalog for the application + if it isn't found in the desktop database. + spec output: FSSpec record containing the application name and + location. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTGetAPPL +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DTGetAPPL( + ConstStr255Param volName, + short vRefNum, + OSType creator, + short * applVRefNum, + long * applParID, + Str255 applName); + + +/* + The DTGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails, then it tries to find an application with the specified creator + using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + applVRefNum output: The volume reference number of the volume the + application is on. + applParID output: The parent directory ID of the application. + applName output: The name of the application. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTGetAPPL +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDTGetAPPL( + ConstStr255Param volName, + short vRefNum, + OSType creator, + FSSpec * spec); + + +/* + The FSpDTGetAPPL function finds an application (file type 'APPL') with + the specified creator on the specified volume. It first tries to get + the application mapping from the desktop database. If that fails, + then it tries to find an application in the Desktop file. If that + fails, then it tries to find an application with the specified creator + using the File Manager's CatSearch routine. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The file's creator type. + spec output: FSSpec record containing the application name and + location. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 No default volume + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTGetAPPL +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DTGetIcon( + ConstStr255Param volName, + short vRefNum, + short iconType, + OSType fileCreator, + OSType fileType, + Handle * iconHandle); + + +/* + The DTGetIcon function retrieves the specified icon and returns it in + a newly created handle. The icon is retrieves from the Desktop Manager + or if the Desktop Manager is not available, from the Finder's Desktop + file. Your program is responsible for disposing of the handle when it is + done using the icon. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + iconType input: The icon type as defined in Files.h. Valid values are: + kLargeIcon + kLarge4BitIcon + kLarge8BitIcon + kSmallIcon + kSmall4BitIcon + kSmall8BitIcon + fileCreator input: The icon's creator type. + fileType input: The icon's file type. + iconHandle output: A Handle containing the newly created icon. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + paramErr -50 Volume doesn't support this function + rfNumErr -51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call + memFullErr -108 iconHandle could not be allocated + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DTSetComment( + short vRefNum, + long dirID, + ConstStr255Param name, + ConstStr255Param comment); + + +/* + The DTSetComment function sets a file or directory's Finder comment + field. The volume must support the Desktop Manager because you only + have read access to the Desktop file. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + comment input: The comment to add. Comments are limited to 200 characters; + longer comments are truncated. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + paramErr -50 Volume doesn't support this function + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + rfNumErr Ð51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, FSpDTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDTSetComment( + const FSSpec * spec, + ConstStr255Param comment); + + +/* + The FSpDTSetComment function sets a file or directory's Finder comment + field. The volume must support the Desktop Manager because you only + have read access to the Desktop file. + + spec input: An FSSpec record specifying the file or directory. + comment input: The comment to add. Comments are limited to 200 characters; + longer comments are truncated. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + rfNumErr Ð51 Reference number invalid + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, DTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DTGetComment( + short vRefNum, + long dirID, + ConstStr255Param name, + Str255 comment); + + +/* + The DTGetComment function gets a file or directory's Finder comment + field (if any) from the Desktop Manager or if the Desktop Manager is + not available, from the Finder's Desktop file. + + IMPORTANT NOTE: Inside Macintosh says that comments are up to + 200 characters. While that may be correct for the HFS file system's + Desktop Manager, other file systems (such as Apple Photo Access) return + up to 255 characters. Make sure the comment buffer is a Str255 or you'll + regret it. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + comment output: A Str255 where the comment is to be returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, DTSetComment, FSpDTSetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDTGetComment( + const FSSpec * spec, + Str255 comment); + + +/* + The FSpDTGetComment function gets a file or directory's Finder comment + field (if any) from the Desktop Manager or if the Desktop Manager is + not available, from the Finder's Desktop file. + + IMPORTANT NOTE: Inside Macintosh says that comments are up to + 200 characters. While that may be correct for the HFS file system's + Desktop Manager, other file systems (such as Apple Photo Access) return + up to 255 characters. Make sure the comment buffer is a Str255 or you'll + regret it. + + spec input: An FSSpec record specifying the file or directory. + comment output: A Str255 where the comment is to be returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTCopyComment, FSpDTCopyComment, DTSetComment, FSpDTSetComment, + DTGetComment +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DTCopyComment( + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName); + + +/* + The DTCopyComment function copies the file or folder comment from the + source to the destination object. The destination volume must support + the Desktop Manager because you only have read access to the Desktop file. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Pointer to source object name, or nil when srcDirID + specifies a directory that's the object. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination object name, or nil when + dstDirID specifies a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: FSpDTCopyComment, DTSetComment, FSpDTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpDTCopyComment( + const FSSpec * srcSpec, + const FSSpec * dstSpec); + + +/* + The FSpDTCopyComment function copies the desktop database comment from + the source to the destination object. Both the source and the + destination volumes must support the Desktop Manager. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination object. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr Ð43 File or directory doesnÕt exist + wPrErr Ð44 Volume is locked through hardware + vLckdErr Ð46 Volume is locked through software + paramErr -50 Volume doesn't support this function + rfNumErr Ð51 Reference number invalid + paramErr -50 Volume doesn't support this function + extFSErr -58 External file system error - no file + system claimed this call. + desktopDamagedErr -1305 The desktop database has become corrupted - + the Finder will fix this, but if your + application is not running with the + Finder, use PBDTReset or PBDTDelete + afpItemNotFound -5012 Information not found + + __________ + + Also see: DTCopyComment, DTSetComment, FSpDTSetComment, DTGetComment, + FSpDTGetComment +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MOREDESKTOPMGR__ */ + diff --git a/src/osx/carbon/morefile/MoreFiles.c b/src/osx/carbon/morefile/MoreFiles.c new file mode 100644 index 0000000000..8426bdaffe --- /dev/null +++ b/src/osx/carbon/morefile/MoreFiles.c @@ -0,0 +1,643 @@ +/* + File: MoreFiles.c + + Contains: The long lost high-level and FSSpec File Manager functions. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFiles.h" +#include "MoreFilesExtras.h" + +/*****************************************************************************/ + +pascal OSErr HGetVolParms(ConstStr255Param volName, + short vRefNum, + GetVolParmsInfoBuffer *volParmsInfo, + long *infoSize) +{ + HParamBlockRec pb; + OSErr error; + + pb.ioParam.ioNamePtr = (StringPtr)volName; + pb.ioParam.ioVRefNum = vRefNum; + pb.ioParam.ioBuffer = (Ptr)volParmsInfo; + pb.ioParam.ioReqCount = *infoSize; + error = PBHGetVolParmsSync(&pb); + if ( error == noErr ) + { + *infoSize = pb.ioParam.ioActCount; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HCreateMinimum(short vRefNum, + long dirID, + ConstStr255Param fileName) +{ + HParamBlockRec pb; + + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.ioParam.ioVersNum = 0; + pb.fileParam.ioDirID = dirID; + return ( PBHCreateSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCreateMinimum(const FSSpec *spec) +{ + return ( HCreateMinimum(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr ExchangeFiles(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstName) +{ + HParamBlockRec pb; + + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioSrcDirID = srcDirID; + pb.fidParam.ioNamePtr = (StringPtr)srcName; + pb.fidParam.ioDestDirID = dstDirID; + pb.fidParam.ioDestNamePtr = (StringPtr)dstName; + return ( PBExchangeFilesSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr ResolveFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID, + long *parID, + StringPtr fileName) +{ + HParamBlockRec pb; + OSErr error; + Str255 tempStr; + + tempStr[0] = 0; + if ( volName != NULL ) + { + BlockMoveData(volName, tempStr, volName[0] + 1); + } + pb.fidParam.ioNamePtr = (StringPtr)tempStr; + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioFileID = fileID; + error = PBResolveFileIDRefSync(&pb); + if ( error == noErr ) + { + *parID = pb.fidParam.ioSrcDirID; + if ( fileName != NULL ) + { + BlockMoveData(tempStr, fileName, tempStr[0] + 1); + } + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpResolveFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID, + FSSpec *spec) +{ + OSErr error; + + error = DetermineVRefNum(volName, vRefNum, &(spec->vRefNum)); + if ( error == noErr ) + { + error = ResolveFileIDRef(volName, vRefNum, fileID, &(spec->parID), spec->name); + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr CreateFileIDRef(short vRefNum, + long parID, + ConstStr255Param fileName, + long *fileID) +{ + HParamBlockRec pb; + OSErr error; + + pb.fidParam.ioNamePtr = (StringPtr)fileName; + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioSrcDirID = parID; + error = PBCreateFileIDRefSync(&pb); + if ( (error == noErr) || (error == fidExists) || (error == afpIDExists) ) + { + *fileID = pb.fidParam.ioFileID; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCreateFileIDRef(const FSSpec *spec, + long *fileID) +{ + return ( CreateFileIDRef(spec->vRefNum, spec->parID, spec->name, fileID) ); +} + +/*****************************************************************************/ + +pascal OSErr DeleteFileIDRef(ConstStr255Param volName, + short vRefNum, + long fileID) +{ + HParamBlockRec pb; + + pb.fidParam.ioNamePtr = (StringPtr)volName; + pb.fidParam.ioVRefNum = vRefNum; + pb.fidParam.ioFileID = fileID; + return ( PBDeleteFileIDRefSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FlushFile(short refNum) +{ + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + return ( PBFlushFileSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr LockRange(short refNum, + long rangeLength, + long rangeStart) +{ + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + return ( PBLockRangeSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr UnlockRange(short refNum, + long rangeLength, + long rangeStart) +{ + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + return ( PBUnlockRangeSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr GetForeignPrivs(short vRefNum, + long dirID, + ConstStr255Param name, + void *foreignPrivBuffer, + long *foreignPrivSize, + long *foreignPrivInfo1, + long *foreignPrivInfo2, + long *foreignPrivInfo3, + long *foreignPrivInfo4) +{ + HParamBlockRec pb; + OSErr error; + + pb.foreignPrivParam.ioNamePtr = (StringPtr)name; + pb.foreignPrivParam.ioVRefNum = vRefNum; + pb.foreignPrivParam.ioForeignPrivDirID = dirID; + pb.foreignPrivParam.ioForeignPrivBuffer = (Ptr)foreignPrivBuffer; + pb.foreignPrivParam.ioForeignPrivReqCount = *foreignPrivSize; + error = PBGetForeignPrivsSync(&pb); + *foreignPrivSize = pb.foreignPrivParam.ioForeignPrivActCount; + *foreignPrivInfo1 = pb.foreignPrivParam.ioForeignPrivInfo1; + *foreignPrivInfo2 = pb.foreignPrivParam.ioForeignPrivInfo2; + *foreignPrivInfo3 = pb.foreignPrivParam.ioForeignPrivInfo3; + *foreignPrivInfo4 = pb.foreignPrivParam.ioForeignPrivInfo4; + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetForeignPrivs(const FSSpec *spec, + void *foreignPrivBuffer, + long *foreignPrivSize, + long *foreignPrivInfo1, + long *foreignPrivInfo2, + long *foreignPrivInfo3, + long *foreignPrivInfo4) +{ + return ( GetForeignPrivs(spec->vRefNum, spec->parID, spec->name, + foreignPrivBuffer, foreignPrivSize, + foreignPrivInfo1, foreignPrivInfo2, + foreignPrivInfo3, foreignPrivInfo4) ); +} + +/*****************************************************************************/ + +pascal OSErr SetForeignPrivs(short vRefNum, + long dirID, + ConstStr255Param name, + const void *foreignPrivBuffer, + long *foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4) +{ + HParamBlockRec pb; + OSErr error; + + pb.foreignPrivParam.ioNamePtr = (StringPtr)name; + pb.foreignPrivParam.ioVRefNum = vRefNum; + pb.foreignPrivParam.ioForeignPrivDirID = dirID; + pb.foreignPrivParam.ioForeignPrivBuffer = (Ptr)foreignPrivBuffer; + pb.foreignPrivParam.ioForeignPrivReqCount = *foreignPrivSize; + pb.foreignPrivParam.ioForeignPrivInfo1 = foreignPrivInfo1; + pb.foreignPrivParam.ioForeignPrivInfo2 = foreignPrivInfo2; + pb.foreignPrivParam.ioForeignPrivInfo3 = foreignPrivInfo3; + pb.foreignPrivParam.ioForeignPrivInfo4 = foreignPrivInfo4; + error = PBSetForeignPrivsSync(&pb); + if ( error == noErr ) + { + *foreignPrivSize = pb.foreignPrivParam.ioForeignPrivActCount; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetForeignPrivs(const FSSpec *spec, + const void *foreignPrivBuffer, + long *foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4) +{ + return ( SetForeignPrivs(spec->vRefNum, spec->parID, spec->name, + foreignPrivBuffer, foreignPrivSize, + foreignPrivInfo1, foreignPrivInfo2, + foreignPrivInfo3, foreignPrivInfo4) ); +} + +/*****************************************************************************/ + +pascal OSErr HGetLogInInfo(ConstStr255Param volName, + short vRefNum, + short *loginMethod, + StringPtr userName) +{ + HParamBlockRec pb; + OSErr error; + + pb.objParam.ioNamePtr = (StringPtr)volName; + pb.objParam.ioVRefNum = vRefNum; + pb.objParam.ioObjNamePtr = userName; + error = PBHGetLogInInfoSync(&pb); + if ( error == noErr ) + { + *loginMethod = pb.objParam.ioObjType; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HGetDirAccess(short vRefNum, + long dirID, + ConstStr255Param name, + long *ownerID, + long *groupID, + long *accessRights) +{ + HParamBlockRec pb; + OSErr error; + + pb.accessParam.ioNamePtr = (StringPtr)name; + pb.accessParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + error = PBHGetDirAccessSync(&pb); + if ( error == noErr ) + { + *ownerID = pb.accessParam.ioACOwnerID; + *groupID = pb.accessParam.ioACGroupID; + *accessRights = pb.accessParam.ioACAccess; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDirAccess(const FSSpec *spec, + long *ownerID, + long *groupID, + long *accessRights) +{ + return ( HGetDirAccess(spec->vRefNum, spec->parID, spec->name, + ownerID, groupID, accessRights) ); +} + +/*****************************************************************************/ + +pascal OSErr HSetDirAccess(short vRefNum, + long dirID, + ConstStr255Param name, + long ownerID, + long groupID, + long accessRights) +{ + HParamBlockRec pb; + + pb.accessParam.ioNamePtr = (StringPtr)name; + pb.accessParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + pb.accessParam.ioACOwnerID = ownerID; + pb.accessParam.ioACGroupID = groupID; + pb.accessParam.ioACAccess = accessRights; + return ( PBHSetDirAccessSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetDirAccess(const FSSpec *spec, + long ownerID, + long groupID, + long accessRights) +{ + return ( HSetDirAccess(spec->vRefNum, spec->parID, spec->name, + ownerID, groupID, accessRights) ); +} + +/*****************************************************************************/ + +pascal OSErr HMapID(ConstStr255Param volName, + short vRefNum, + long ugID, + short objType, + StringPtr name) +{ + HParamBlockRec pb; + + pb.objParam.ioNamePtr = (StringPtr)volName; + pb.objParam.ioVRefNum = vRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = name; + pb.objParam.ioObjID = ugID; + return ( PBHMapIDSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr HMapName(ConstStr255Param volName, + short vRefNum, + ConstStr255Param name, + short objType, + long *ugID) +{ + HParamBlockRec pb; + OSErr error; + + pb.objParam.ioNamePtr = (StringPtr)volName; + pb.objParam.ioVRefNum = vRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = (StringPtr)name; + error = PBHMapNameSync(&pb); + if ( error == noErr ) + { + *ugID = pb.objParam.ioObjID; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HCopyFile(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName) +{ + HParamBlockRec pb; + + pb.copyParam.ioVRefNum = srcVRefNum; + pb.copyParam.ioDirID = srcDirID; + pb.copyParam.ioNamePtr = (StringPtr)srcName; + pb.copyParam.ioDstVRefNum = dstVRefNum; + pb.copyParam.ioNewDirID = dstDirID; + pb.copyParam.ioNewName = (StringPtr)dstPathname; + pb.copyParam.ioCopyName = (StringPtr)copyName; + return ( PBHCopyFileSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyFile(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName) +{ + return ( HCopyFile(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, + dstSpec->name, copyName) ); +} + +/*****************************************************************************/ + +pascal OSErr HMoveRename(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName) +{ + HParamBlockRec pb; + + pb.copyParam.ioVRefNum = vRefNum; + pb.copyParam.ioDirID = srcDirID; + pb.copyParam.ioNamePtr = (StringPtr)srcName; + pb.copyParam.ioNewDirID = dstDirID; + pb.copyParam.ioNewName = (StringPtr)dstpathName; + pb.copyParam.ioCopyName = (StringPtr)copyName; + return ( PBHMoveRenameSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpMoveRename(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName) +{ + OSErr error; + + /* make sure the FSSpecs refer to the same volume */ + if ( srcSpec->vRefNum != dstSpec->vRefNum ) + { + error = diffVolErr; + } + else + { + error = HMoveRename(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->parID, dstSpec->name, copyName); + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetVolMountInfoSize(ConstStr255Param volName, + short vRefNum, + short *size) +{ + ParamBlockRec pb; + + pb.ioParam.ioNamePtr = (StringPtr)volName; + pb.ioParam.ioVRefNum = vRefNum; + pb.ioParam.ioBuffer = (Ptr)size; + return ( PBGetVolMountInfoSize(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr GetVolMountInfo(ConstStr255Param volName, + short vRefNum, + void *volMountInfo) +{ + ParamBlockRec pb; + + pb.ioParam.ioNamePtr = (StringPtr)volName; + pb.ioParam.ioVRefNum = vRefNum; + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + return ( PBGetVolMountInfo(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr VolumeMount(const void *volMountInfo, + short *vRefNum) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + error = PBVolumeMount(&pb); + if ( error == noErr ) + { + *vRefNum = pb.ioParam.ioVRefNum; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr Share(short vRefNum, + long dirID, + ConstStr255Param name) +{ + HParamBlockRec pb; + + pb.fileParam.ioNamePtr = (StringPtr)name; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + return ( PBShareSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpShare(const FSSpec *spec) +{ + return ( Share(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr Unshare(short vRefNum, + long dirID, + ConstStr255Param name) +{ + HParamBlockRec pb; + + pb.fileParam.ioNamePtr = (StringPtr)name; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + return ( PBUnshareSync(&pb) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpUnshare(const FSSpec *spec) +{ + return ( Unshare(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr GetUGEntry(short objType, + StringPtr objName, + long *objID) +{ + HParamBlockRec pb; + OSErr error; + + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = objName; + pb.objParam.ioObjID = *objID; + error = PBGetUGEntrySync(&pb); + if ( error == noErr ) + { + *objID = pb.objParam.ioObjID; + } + return ( error ); +} + +/*****************************************************************************/ diff --git a/src/osx/carbon/morefile/MoreFiles.h b/src/osx/carbon/morefile/MoreFiles.h new file mode 100644 index 0000000000..25b1fcf13e --- /dev/null +++ b/src/osx/carbon/morefile/MoreFiles.h @@ -0,0 +1,1420 @@ +/* + File: MoreFiles.h + + Contains: The long lost high-level and FSSpec File Manager functions. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __MOREFILES__ +#define __MOREFILES__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HGetVolParms( + ConstStr255Param volName, + short vRefNum, + GetVolParmsInfoBuffer * volParmsInfo, + long * infoSize); + + +/* + The HGetVolParms function returns information about the characteristics + of a volume. A result of paramErr usually just means the volume doesn't + support PBHGetVolParms and the feature you were going to check + for isn't available. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + volParmsInfo input: Pointer to GetVolParmsInfoBuffer where the + volume attributes information is returned. + output: Atributes information. + infoSize input: Size of buffer pointed to by volParmsInfo. + output: Size of data actually returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume doesn't support this function + + __________ + + Also see the macros for checking attribute bits in MoreFilesExtras.h +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HCreateMinimum( + short vRefNum, + long dirID, + ConstStr255Param fileName); + + +/* + The HCreateMinimum function creates a new file without attempting to set + the creator and file type of the new file. This function is needed to + create a file in an AppleShare "drop box" where the user can make + changes, but cannot see folder or files. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: The name of the new file. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 A directory exists with that name + + __________ + + Also see: FSpCreateMinimum +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCreateMinimum(const FSSpec * spec); + + +/* + The FSpCreateMinimum function creates a new file without attempting to set + the the creator and file type of the new file. This function is needed to + create a file in an AppleShare "dropbox" where the user can make + changes, but cannot see folder or files. + + spec input: An FSSpec record specifying the file to create. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 Directory not found or incomplete pathname + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + dupFNErr -48 Duplicate filename and version + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 A directory exists with that name + + __________ + + Also see: HCreateMinimum +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ExchangeFiles( + short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstName); + + +/* + The ExchangeFiles function swaps the data in two files on the same + volume by changing some of the information in the volume catalog and, + if the files are open, in the file control blocks. + + vRefNum input: Volume specification. + srcDirID input: Source directory ID. + srcName input: Source file name. + dstDirID input: Destination directory ID. + dstName input: Destination file name. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + wrgVolTypErr -123 Not an HFS volume + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Object is a directory, not a file + afpSameObjectErr -5038 Source and destination are the same + + __________ + + Also see: FSpExchangeFilesCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ResolveFileIDRef( + ConstStr255Param volName, + short vRefNum, + long fileID, + long * parID, + StringPtr fileName); + + +/* + The ResolveFileIDRef function returns the filename and parent directory ID + of the file with the specified file ID reference. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileID input: The file ID reference. + parID output: The parent directory ID of the file. + name input: Points to a buffer (minimum Str63) where the filename + is to be returned or must be nil. + output: The filename. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidNotFoundErr -1300 File ID reference not found + notAFileErr -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDNotFound -5034 File ID reference not found + afpBadIDErr -5039 File ID reference not found + + __________ + + Also see: FSpResolveFileIDRef, CreateFileIDRef, FSpCreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpResolveFileIDRef( + ConstStr255Param volName, + short vRefNum, + long fileID, + FSSpecPtr spec); + + +/* + The FSpResolveFileIDRef function fills in an FSSpec with the location + of the file with the specified file ID reference. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileID input: The file ID reference. + spec input: A pointer to a FSSpec record. + output: A file system specification to be filled in by + FSpResolveFileIDRef. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + paramErr -50 Function not supported by volume or + no default volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidNotFoundErr -1300 File ID reference not found + notAFileErr -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDNotFound -5034 File ID reference not found + afpBadIDErr -5039 File ID reference not found + + __________ + + Also see: ResolveFileIDRef, CreateFileIDRef, FSpCreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +CreateFileIDRef( + short vRefNum, + long parID, + ConstStr255Param fileName, + long * fileID); + + +/* + The CreateFileIDRef function creates a file ID reference for the + specified file, or if a file ID reference already exists, supplies + the file ID reference and returns the result code fidExists or afpIDExists. + + vRefNum input: Volume specification. + parID input: Directory ID. + fileName input: The name of the file. + fileID output: The file ID reference (if result is noErr, + fidExists, or afpIDExists). + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidExists -1301 File ID reference already exists + notAFileErrn -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDExists -5035 File ID reference already exists + + __________ + + Also see: FSpResolveFileIDRef, ResolveFileIDRef, FSpCreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCreateFileIDRef( + const FSSpec * spec, + long * fileID); + + +/* + The FSpCreateFileIDRef function creates a file ID reference for the + specified file, or if a file ID reference already exists, supplies + the file ID reference and returns the result code fidExists or afpIDExists. + + spec input: An FSSpec record specifying the file. + fileID output: The file ID reference (if result is noErr, + fidExists, or afpIDExists). + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Not an HFS volume + fidExists -1301 File ID reference already exists + notAFileErrn -1302 Specified file is a directory + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDExists -5035 File ID reference already exists + + __________ + + Also see: FSpResolveFileIDRef, ResolveFileIDRef, CreateFileIDRef, + DeleteFileIDRef +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DeleteFileIDRef( + ConstStr255Param volName, + short vRefNum, + long fileID); + + +/* + The DeleteFileIDRef function deletes a file ID reference. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileID input: The file ID reference. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + vLckdErr -46 Software volume lock + paramErr -50 Function not supported by volume + volOfflinErr -53 Volume is offline + extFSErr -58 External file system error - no file + system claimed this call. + wrgVolTypErr -123 Function is not supported by volume + fidNotFoundErr -1300 File ID reference not found + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Specified file is a directory + afpIDNotFound -5034 File ID reference not found + + __________ + + Also see: FSpResolveFileIDRef, ResolveFileIDRef, CreateFileIDRef, + FSpCreateFileIDRef +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FlushFile(short refNum); + + +/* + The FlushFile function writes the contents of a file's access path + buffer (the fork data) to the volume. Note: some of the file's catalog + information stored on the volume may not be correct until FlushVol + is called. + + refNum input: The file reference number of an open file. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + fnOpnErr -38 File not open + fnfErr -43 File not found + rfNumErr -51 Bad reference number + extFSErr -58 External file system error - no file + system claimed this call. +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +LockRange( + short refNum, + long rangeLength, + long rangeStart); + + +/* + The LockRange function locks (denies access to) a portion of a file + that was opened with shared read/write permission. + + refNum input: The file reference number of an open file. + rangeLength input: The number of bytes in the range. + rangeStart input: The starting byte in the range to lock. + + Result Codes + noErr 0 No error + ioErr -36 I/O error + fnOpnErr -38 File not open + eofErr -39 Logical end-of-file reached + fLckdErr -45 File is locked by another user + paramErr -50 Negative ioReqCount + rfNumErr -51 Bad reference number + extFSErr -58 External file system error - no file + system claimed this call. + volGoneErr -124 Server volume has been disconnected + afpNoMoreLocks -5015 No more ranges can be locked + afpRangeOverlap -5021 Part of range is already locked + + __________ + + Also see: UnlockRange +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +UnlockRange( + short refNum, + long rangeLength, + long rangeStart); + + +/* + The UnlockRange function unlocks (allows access to) a previously locked + portion of a file that was opened with shared read/write permission. + + refNum input: The file reference number of an open file. + rangeLength input: The number of bytes in the range. + rangeStart input: The starting byte in the range to unlock. + + Result Codes + noErr 0 No error + ioErr -36 I/O error + fnOpnErr -38 File not open + eofErr -39 Logical end-of-file reached + paramErr -50 Negative ioReqCount + rfNumErr -51 Bad reference number + extFSErr -58 External file system error - no file + system claimed this call. + volGoneErr -124 Server volume has been disconnected + afpRangeNotLocked -5020 Specified range was not locked + + __________ + + Also see: LockRange +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetForeignPrivs( + short vRefNum, + long dirID, + ConstStr255Param name, + void * foreignPrivBuffer, + long * foreignPrivSize, + long * foreignPrivInfo1, + long * foreignPrivInfo2, + long * foreignPrivInfo3, + long * foreignPrivInfo4); + + +/* + The GetForeignPrivs function retrieves the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + foreignPrivBuffer input: Pointer to buffer where the privilege + information is returned. + output: Privilege information. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 output: Information specific to privilege model. + foreignPrivInfo2 output: Information specific to privilege model. + foreignPrivInfo3 output: Information specific to privilege model. + foreignPrivInfo4 output: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: FSpGetForeignPrivs, SetForeignPrivs, FSpSetForeignPrivs +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetForeignPrivs( + const FSSpec * spec, + void * foreignPrivBuffer, + long * foreignPrivSize, + long * foreignPrivInfo1, + long * foreignPrivInfo2, + long * foreignPrivInfo3, + long * foreignPrivInfo4); + + +/* + The FSpGetForeignPrivs function retrieves the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + spec input: An FSSpec record specifying the object. + foreignPrivBuffer input: Pointer to buffer where the privilege + information is returned. + output: Privilege information. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 output: Information specific to privilege model. + foreignPrivInfo2 output: Information specific to privilege model. + foreignPrivInfo3 output: Information specific to privilege model. + foreignPrivInfo4 output: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: GetForeignPrivs, SetForeignPrivs, FSpSetForeignPrivs +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +SetForeignPrivs( + short vRefNum, + long dirID, + ConstStr255Param name, + const void * foreignPrivBuffer, + long * foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4); + + +/* + The SetForeignPrivs function changes the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + foreignPrivBuffer input: Pointer to privilege information buffer. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 input: Information specific to privilege model. + foreignPrivInfo2 input: Information specific to privilege model. + foreignPrivInfo3 input: Information specific to privilege model. + foreignPrivInfo4 input: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: GetForeignPrivs, FSpGetForeignPrivs, FSpSetForeignPrivs +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetForeignPrivs( + const FSSpec * spec, + const void * foreignPrivBuffer, + long * foreignPrivSize, + long foreignPrivInfo1, + long foreignPrivInfo2, + long foreignPrivInfo3, + long foreignPrivInfo4); + + +/* + The FSpSetForeignPrivs function changes the native access-control + information for a file or directory stored on a volume managed by + a foreign file system. + + spec input: An FSSpec record specifying the object. + foreignPrivBuffer input: Pointer to privilege information buffer. + foreignPrivSize input: Size of buffer pointed to by + foreignPrivBuffer. + output: Amount of buffer actually used. + foreignPrivInfo1 input: Information specific to privilege model. + foreignPrivInfo2 input: Information specific to privilege model. + foreignPrivInfo3 input: Information specific to privilege model. + foreignPrivInfo4 input: Information specific to privilege model. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Volume is HFS or MFS (that is, it has + no foreign privilege model), or foreign + volume does not support these calls + + __________ + + Also see: GetForeignPrivs, FSpGetForeignPrivs, SetForeignPrivs +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HGetLogInInfo( + ConstStr255Param volName, + short vRefNum, + short * loginMethod, + StringPtr userName); + + +/* + The HGetLogInInfo function retrieves the login method and user name + used to log on to a particular shared volume. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: The volume reference number. + loginMethod output: The login method used (kNoUserAuthentication, + kPassword, kEncryptPassword, or + kTwoWayEncryptPassword). + userName input: Points to a buffer (minimum Str31) where the user + name is to be returned or must be nil. + output: The user name. + + Result Codes + noErr 0 No error + nsvErr -35 Specified volume doesnÕt exist + paramErr -50 Function not supported by volume + + __________ + + Also see: HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HGetDirAccess( + short vRefNum, + long dirID, + ConstStr255Param name, + long * ownerID, + long * groupID, + long * accessRights); + + +/* + The HGetDirAccess function retrieves the directory access control + information for a directory on a shared volume. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + ownerID output: The directory's owner ID. + groupID output: The directory's group ID or + 0 if no group affiliation. + accessRights output: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + paramErr -50 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + to the directory + + __________ + + Also see: HGetLogInInfo, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetDirAccess( + const FSSpec * spec, + long * ownerID, + long * groupID, + long * accessRights); + + +/* + The FSpGetDirAccess function retrieves the directory access control + information for a directory on a shared volume. + + spec input: An FSSpec record specifying the directory. + ownerID output: The directory's owner ID. + groupID output: The directory's group ID or + 0 if no group affiliation. + accessRights output: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + paramErr -50 Function not supported by volume + afpAccessDenied -5000 User does not have the correct access + to the directory + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HSetDirAccess( + short vRefNum, + long dirID, + ConstStr255Param name, + long ownerID, + long groupID, + long accessRights); + + +/* + The HSetDirAccess function changes the directory access control + information for a directory on a shared volume. You must own a directory + to change its access control information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + ownerID input: The directory's owner ID. + groupID input: The directory's group ID or + 0 if no group affiliation. + accessRights input: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Parameter error + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, + FSpSetDirAccess, HMapName, HMapID +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetDirAccess( + const FSSpec * spec, + long ownerID, + long groupID, + long accessRights); + + +/* + The FSpSetDirAccess function changes the directory access control + information for a directory on a shared volume. You must own a directory + to change its access control information. + + spec input: An FSSpec record specifying the directory. + ownerID input: The directory's owner ID. + groupID input: The directory's group ID or + 0 if no group affiliation. + accessRights input: The directory's access rights. + + Result Codes + noErr 0 No error + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Parameter error + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + HMapName, HMapID +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HMapID( + ConstStr255Param volName, + short vRefNum, + long ugID, + short objType, + StringPtr name); + + +/* + The HMapID function determines the name of a user or group if you know + the user or group ID. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + objType input: The mapping function code: 1 if you're mapping a + user ID to a user name or 2 if you're mapping a + group ID to a group name. + name input: Points to a buffer (minimum Str31) where the user + or group name is to be returned or must be nil. + output: The user or group name. + + Result Codes + noErr 0 No error + fnfErr -43 Unrecognizable owner or group name + paramErr -50 Function not supported by volume + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapName +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HMapName( + ConstStr255Param volName, + short vRefNum, + ConstStr255Param name, + short objType, + long * ugID); + + +/* + The HMapName function determines the user or group ID if you know the + user or group name. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + name input: The user or group name. + objType input: The mapping function code: 3 if you're mapping a + user name to a user ID or 4 if you're mapping a + group name to a group ID. + ugID output: The user or group ID. + + Result Codes + noErr 0 No error + fnfErr -43 Unrecognizable owner or group name + paramErr -50 Function not supported by volume + + __________ + + Also see: HGetLogInInfo, HGetDirAccess, FSpGetDirAccess, HSetDirAccess, + FSpSetDirAccess, HMapID +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HCopyFile( + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstPathname, + ConstStr255Param copyName); + + +/* + The HCopyFile function duplicates a file and optionally renames it. + The source and destination volumes must be on the same file server. + This function instructs the server to copy the file. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Source file name. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstPathname input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new file name if the file is to be + renamed or nil if the file isn't to be renamed. + + Result Codes + noErr 0 No error + dskFulErr -34 Destination volume is full + fnfErr -43 Source file not found, or destination + directory does not exist + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + paramErr -50 Function not supported by volume + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 The user does not have the right to + read the source or write to the + destination + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory + + __________ + + Also see: FSpCopyFile, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCopyFile( + const FSSpec * srcSpec, + const FSSpec * dstSpec, + ConstStr255Param copyName); + + +/* + The FSpCopyFile function duplicates a file and optionally renames it. + The source and destination volumes must be on the same file server. + This function instructs the server to copy the file. + + srcSpec input: An FSSpec record specifying the source file. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new file name if the file is to be + renamed or nil if the file isn't to be renamed. + + Result Codes + noErr 0 No error + dskFulErr -34 Destination volume is full + fnfErr -43 Source file not found, or destination + directory does not exist + vLckdErr -46 Destination volume is read-only + fBsyErr -47 The source or destination file could + not be opened with the correct access + modes + dupFNErr -48 Destination file already exists + paramErr -50 Function not supported by volume + wrgVolTypErr -123 Function not supported by volume + afpAccessDenied -5000 The user does not have the right to + read the source or write to the + destination + afpDenyConflict -5006 The source or destination file could + not be opened with the correct access + modes + afpObjectTypeErr -5025 Source is a directory + + __________ + + Also see: HCopyFile, FileCopy, FSpFileCopy +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HMoveRename( + short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName); + + +/* + The HMoveRename function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + shared volume. + + vRefNum input: Volume specification. + srcDirID input: Source directory ID. + srcName input: The source object name. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + fnfErr -43 Source file or directory not found + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + dupFNErr -48 Destination already exists + paramErr -50 Function not supported by volume + badMovErr -122 Attempted to move directory into + offspring + afpAccessDenied -5000 The user does not have the right to + move the file or directory + + __________ + + Also see: FSpMoveRename, HMoveRenameCompat, FSpMoveRenameCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpMoveRename( + const FSSpec * srcSpec, + const FSSpec * dstSpec, + ConstStr255Param copyName); + + +/* + The FSpMoveRename function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + shared volume. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + fnfErr -43 Source file or directory not found + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + dupFNErr -48 Destination already exists + paramErr -50 Function not supported by volume + badMovErr -122 Attempted to move directory into + offspring + afpAccessDenied -5000 The user does not have the right to + move the file or directory + + __________ + + Also see: HMoveRename, HMoveRenameCompat, FSpMoveRenameCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetVolMountInfoSize( + ConstStr255Param volName, + short vRefNum, + short * size); + + +/* + The GetVolMountInfoSize function determines the how much space the + program needs to allocate for a volume mounting information record. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + size output: The space needed (in bytes) of the volume mounting + information record. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Parameter error + extFSErr -58 External file system error - no file + system claimed this call. + + __________ + + Also see: GetVolMountInfo, VolumeMount BuildAFPVolMountInfo, + RetrieveAFPVolMountInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetVolMountInfo( + ConstStr255Param volName, + short vRefNum, + void * volMountInfo); + + +/* + The GetVolMountInfo function retrieves a volume mounting information + record containing all the information needed to mount the volume, + except for passwords. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + volMountInfo output: Points to a volume mounting information + record where the mounting information is to + be returned. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + paramErr -50 Parameter error + extFSErr -58 External file system error - no file + system claimed this call. + + __________ + + Also see: GetVolMountInfoSize, VolumeMount, BuildAFPVolMountInfo, + RetrieveAFPVolMountInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +VolumeMount( + const void * volMountInfo, + short * vRefNum); + + +/* + The VolumeMount function mounts a volume using a volume mounting + information record. + + volMountInfo input: Points to a volume mounting information record. + vRefNum output: A volume reference number. + + Result Codes + noErr 0 No error + notOpenErr -28 AppleTalk is not open + nsvErr -35 Volume not found + paramErr -50 Parameter error; typically, zone, server, + and volume name combination is not valid + or not complete, or the user name is not + recognized + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Not enough memory to create a new volume + control block for mounting the volume + afpBadUAM -5002 User authentication method is unknown + afpBadVersNum -5003 Workstation is using an AFP version that + the server doesnÕt recognize + afpNoServer -5016 Server is not responding + afpUserNotAuth -5023 User authentication failed (usually, + password is not correct) + afpPwdExpired -5042 Password has expired on server + afpBadDirIDType -5060 Not a fixed directory ID volume + afpCantMountMoreSrvrs -5061 Maximum number of volumes has been + mounted + afpAlreadyMounted -5062 Volume already mounted + afpSameNodeErr -5063 Attempt to log on to a server running + on the same machine + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, BuildAFPVolMountInfo, + RetrieveAFPVolMountInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +Share( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The Share function establishes a local volume or directory as a + share point. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + + Result Codes + noErr 0 No error + tmfoErr -42 Too many share points + fnfErr -43 File not found + dupFNErr -48 Already a share point with this name + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpAccessDenied -5000 This directory cannot be shared + afpObjectTypeErr -5025 Object was a file, not a directory + afpContainsSharedErr -5033 The directory contains a share point + afpInsideSharedErr -5043 The directory is inside a shared directory + + __________ + + Also see: FSpShare, Unshare, FSpUnshare +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpShare(const FSSpec * spec); + + +/* + The FSpShare function establishes a local volume or directory as a + share point. + + spec input: An FSSpec record specifying the share point. + + Result Codes + noErr 0 No error + tmfoErr -42 Too many share points + fnfErr -43 File not found + dupFNErr -48 Already a share point with this name + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpAccessDenied -5000 This directory cannot be shared + afpObjectTypeErr -5025 Object was a file, not a directory + afpContainsSharedErr -5033 The directory contains a share point + afpInsideSharedErr -5043 The directory is inside a shared directory + + __________ + + Also see: Share, Unshare, FSpUnshare +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +Unshare( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The Unshare function removes a share point. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil if dirID + specifies the directory. + + Result Codes + noErr 0 No error + fnfErr -43 File not found + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpObjectTypeErr -5025 Object was a file, not a directory; or, + this directory is not a share point + + __________ + + Also see: Share, FSpShare, FSpUnshare +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpUnshare(const FSSpec * spec); + + +/* + The FSpUnshare function removes a share point. + + spec input: An FSSpec record specifying the share point. + + Result Codes + noErr 0 No error + fnfErr -43 File not found + paramErr -50 Function not supported by volume + dirNFErrdirNFErr -120 Directory not found + afpObjectTypeErr -5025 Object was a file, not a directory; or, + this directory is not a share point + + __________ + + Also see: Share, FSpShare, Unshare +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetUGEntry( + short objType, + StringPtr objName, + long * objID); + + +/* + The GetUGEntry function retrieves user or group entries from the + local file server. + + objType input: The object type: -1 = group; 0 = user + objName input: Points to a buffer (minimum Str31) where the user + or group name is to be returned or must be nil. + output: The user or group name. + objID input: O to get the first user or group. If the entry objID + last returned by GetUGEntry is passed, then user or + group whose alphabetically next in the list of entries + is returned. + output: The user or group ID. + + Result Codes + noErr 0 No error + fnfErr -43 No more users or groups + paramErr -50 Function not supported; or, ioObjID is + negative + + __________ + + Also see: GetUGEntries +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MOREFILES__ */ + diff --git a/src/osx/carbon/morefile/MoreFilesExtras.c b/src/osx/carbon/morefile/MoreFilesExtras.c new file mode 100644 index 0000000000..f9bda4d855 --- /dev/null +++ b/src/osx/carbon/morefile/MoreFilesExtras.c @@ -0,0 +1,3632 @@ +/* + File: MoreFilesExtras.c + + Contains: A collection of useful high-level File Manager routines + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Jim Luther + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL [2500429] Changed null output parameters to real variables when + calling GetSharedLibrary to prevent crashes with older versions + of CFM. Added standard header. Updated names of includes. Added + C function implementations of accessors that used to be macros + since the generated Pascal headers no longer contain + implementations. Updated various other routines to use new + calling convention of the accessor functions. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#if !TARGET_CARBON +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFiles.h" +#include "MoreDesktopMgr.h" +#include "FSpCompat.h" + +#include "MoreFilesExtras.h" + +/*****************************************************************************/ + +/* Functions to get information out of GetVolParmsInfoBuffer. */ + +/* version 1 field getters */ + +pascal short GetVolParmsInfoVersion(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMVersion ); +} + +pascal long GetVolParmsInfoAttrib(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMAttrib ); +} + +pascal Handle GetVolParmsInfoLocalHand(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMLocalHand ); +} + +pascal long GetVolParmsInfoServerAdr(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMServerAdr ); +} + +/* version 2 field getters (assume zero result if version < 2) */ + +pascal long GetVolParmsInfoVolumeGrade(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMVersion >= 2) ? volParms->vMVolumeGrade : 0 ); +} + +pascal long GetVolParmsInfoForeignPrivID(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMVersion >= 2) ? volParms->vMForeignPrivID : 0 ); +} + +/* version 3 field getters (assume zero result if version < 3) */ + +pascal long GetVolParmsInfoExtendedAttributes(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMVersion >= 3) ? volParms->vMExtendedAttributes : 0 ); +} + +/* attribute bits supported by all versions of GetVolParmsInfoBuffer */ + +pascal Boolean isNetworkVolume(const GetVolParmsInfoBuffer *volParms) +{ + return ( volParms->vMServerAdr != 0 ); +} + +pascal Boolean hasLimitFCBs(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bLimitFCBs)) != 0 ); +} + +pascal Boolean hasLocalWList(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bLocalWList)) != 0 ); +} + +pascal Boolean hasNoMiniFndr(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoMiniFndr)) != 0 ); +} + +pascal Boolean hasNoVNEdit(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoVNEdit)) != 0 ); +} + +pascal Boolean hasNoLclSync(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoLclSync)) != 0 ); +} + +pascal Boolean hasTrshOffLine(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bTrshOffLine)) != 0 ); +} + +pascal Boolean hasNoSwitchTo(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoSwitchTo)) != 0 ); +} + +pascal Boolean hasNoDeskItems(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoDeskItems)) != 0 ); +} + +pascal Boolean hasNoBootBlks(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoBootBlks)) != 0 ); +} + +pascal Boolean hasAccessCntl(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bAccessCntl)) != 0 ); +} + +pascal Boolean hasNoSysDir(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bNoSysDir)) != 0 ); +} + +pascal Boolean hasExtFSVol(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasExtFSVol)) != 0 ); +} + +pascal Boolean hasOpenDeny(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasOpenDeny)) != 0 ); +} + +pascal Boolean hasCopyFile(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasCopyFile)) != 0 ); +} + +pascal Boolean hasMoveRename(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasMoveRename)) != 0 ); +} + +pascal Boolean hasDesktopMgr(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasDesktopMgr)) != 0 ); +} + +pascal Boolean hasShortName(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasShortName)) != 0 ); +} + +pascal Boolean hasFolderLock(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasFolderLock)) != 0 ); +} + +pascal Boolean hasPersonalAccessPrivileges(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasPersonalAccessPrivileges)) != 0 ); +} + +pascal Boolean hasUserGroupList(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasUserGroupList)) != 0 ); +} + +pascal Boolean hasCatSearch(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasCatSearch)) != 0 ); +} + +pascal Boolean hasFileIDs(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasFileIDs)) != 0 ); +} + +pascal Boolean hasBTreeMgr(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasBTreeMgr)) != 0 ); +} + +pascal Boolean hasBlankAccessPrivileges(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bHasBlankAccessPrivileges)) != 0 ); +} + +pascal Boolean supportsAsyncRequests(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bSupportsAsyncRequests)) != 0 ); +} + +pascal Boolean supportsTrashVolumeCache(const GetVolParmsInfoBuffer *volParms) +{ + return ( (volParms->vMAttrib & (1L << bSupportsTrashVolumeCache)) != 0 ); +} + +/* attribute bits supported by version 3 and greater versions of GetVolParmsInfoBuffer */ + +pascal Boolean volIsEjectable(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsEjectable)) != 0 ); +} + +pascal Boolean volSupportsHFSPlusAPIs(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsHFSPlusAPIs)) != 0 ); +} + +pascal Boolean volSupportsFSCatalogSearch(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSCatalogSearch)) != 0 ); +} + +pascal Boolean volSupportsFSExchangeObjects(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSExchangeObjects)) != 0 ); +} + +pascal Boolean volSupports2TBFiles(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupports2TBFiles)) != 0 ); +} + +pascal Boolean volSupportsLongNames(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsLongNames)) != 0 ); +} + +pascal Boolean volSupportsMultiScriptNames(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsMultiScriptNames)) != 0 ); +} + +pascal Boolean volSupportsNamedForks(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsNamedForks)) != 0 ); +} + +pascal Boolean volSupportsSubtreeIterators(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSubtreeIterators)) != 0 ); +} + +pascal Boolean volL2PCanMapFileBlocks(const GetVolParmsInfoBuffer *volParms) +{ + return ( (GetVolParmsInfoExtendedAttributes(volParms) & (1L << bL2PCanMapFileBlocks)) != 0 ); +} + +/*****************************************************************************/ + +/* Functions for testing ioACUser bits. */ + +pascal Boolean userIsOwner(SInt8 ioACUser) +{ + return ( (ioACUser & kioACUserNotOwnerMask) == 0 ); +} + +pascal Boolean userHasFullAccess(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserFull ); +} + +pascal Boolean userHasDropBoxAccess(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserDropBox ); +} + +pascal Boolean userHasBulletinBoard(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserBulletinBoard ); +} + +pascal Boolean userHasNoAccess(SInt8 ioACUser) +{ + return ( (ioACUser & acUserAccessMask) == acUserNone ); +} + +/*****************************************************************************/ + +/* local data structures */ + +/* The DeleteEnumGlobals structure is used to minimize the amount of +** stack space used when recursively calling DeleteLevel and to hold +** global information that might be needed at any time. */ + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#endif // PRAGMA_STRUCT_ALIGN +struct DeleteEnumGlobals +{ + OSErr error; /* temporary holder of results - saves 2 bytes of stack each level */ + Str63 itemName; /* the name of the current item */ + UniversalFMPB myPB; /* the parameter block used for PBGetCatInfo calls */ +}; +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#endif // PRAGMA_STRUCT_ALIGN + +typedef struct DeleteEnumGlobals DeleteEnumGlobals; +typedef DeleteEnumGlobals *DeleteEnumGlobalsPtr; + +/*****************************************************************************/ + +/* +** CallPBXGetVolInfoSync is the glue code needed to make PBXGetVolInfoSync +** File Manager requests from CFM-based programs. Apple added PBXGetVolInfoSync +** to InterfaceLib in Mac OS 8.5, so if __MACOSEIGHTFIVEORLATER is defined, +** CallPBXGetVolInfoSync is defined back to PBXGetVolInfoSync. +** +** Non-CFM 68K programs don't needs this glue (and won't get it) because +** they instead use the inline assembly glue found in the Files.h interface +** file. +*/ + +#if TARGET_API_MAC_CARBON || !TARGET_RT_MAC_CFM + + // Carbon builds and 68K builds don't need this glue + #define CallPBXGetVolInfoSync PBXGetVolInfoSync + +#else // TARGET_API_MAC_CARBON || !TARGET_RT_MAC_CFM + + #if __WANTPASCALELIMINATION + #undef pascal + #endif // __WANTPASCALELIMINATION + + /* This is exactly like the simple mixed mode glue in InterfaceLib in Mac OS 8.5 and 8.6 */ + static pascal OSErr PBXGetVolInfoSyncGlue(XVolumeParamPtr paramBlock) + { + enum + { + uppFSDispatchProcInfo = kRegisterBased + | REGISTER_RESULT_LOCATION(kRegisterD0) + | RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) + | REGISTER_ROUTINE_PARAMETER(1, kRegisterD0, SIZE_CODE(sizeof(long))) /* selector */ + | REGISTER_ROUTINE_PARAMETER(2, kRegisterD1, SIZE_CODE(sizeof(long))) /* trap word */ + | REGISTER_ROUTINE_PARAMETER(3, kRegisterA0, SIZE_CODE(sizeof(XVolumeParamPtr))) + }; + + static UniversalProcPtr fsDispatchTrapAddress = NULL; + + /* Is this the first time we've been called? */ + if ( fsDispatchTrapAddress == NULL ) + { + /* Yes - Get the trap address of _FSDispatch */ + fsDispatchTrapAddress = NGetTrapAddress(_FSDispatch, OSTrap); + } + return ( CallOSTrapUniversalProc(fsDispatchTrapAddress, + uppFSDispatchProcInfo, + kFSMXGetVolInfo, + _FSDispatch, + paramBlock) ); + } + + /* + ** PBXGetVolInfoSync was added to the File Manager in System software 7.5.2. + ** However, PBXGetVolInfoSync wasn't added to InterfaceLib until Mac OS 8.5. + ** This wrapper calls PBXGetVolInfoSync if it is found in InterfaceLib; + ** otherwise, it calls PBXGetVolInfoSyncGlue. This ensures that your program + ** is calling the latest implementation of PBXGetVolInfoSync. + */ + static pascal OSErr CallPBXGetVolInfoSync(XVolumeParamPtr paramBlock) + { + typedef pascal OSErr (*PBXGetVolInfoProcPtr) (XVolumeParamPtr paramBlock); + + OSErr result; + CFragConnectionID connID; + Ptr mainAddr; + Str255 errMessage; + static PBXGetVolInfoProcPtr PBXGetVolInfoSyncPtr = NULL; + + //* Is this the first time we've been called? */ + if ( PBXGetVolInfoSyncPtr == NULL ) + { + /* Yes - Get our connection ID to InterfaceLib */ + result = GetSharedLibrary("\pInterfaceLib", kPowerPCCFragArch, kLoadCFrag, &connID, &mainAddr, errMessage); + if ( result == noErr ) + { + /* See if PBXGetVolInfoSync is in InterfaceLib */ + if ( FindSymbol(connID, "\pPBXGetVolInfoSync", &(Ptr)PBXGetVolInfoSyncPtr, NULL) != noErr ) + { + /* Use glue code if symbol isn't found */ + PBXGetVolInfoSyncPtr = PBXGetVolInfoSyncGlue; + } + } + } + /* Call PBXGetVolInfoSync if present; otherwise, call PBXGetVolInfoSyncGlue */ + return ( (*PBXGetVolInfoSyncPtr)(paramBlock) ); + } + + #if __WANTPASCALELIMINATION + #define pascal + #endif // __WANTPASCALELIMINATION + +#endif // TARGET_API_MAC_CARBON || !TARGET_RT_MAC_CFM + +/*****************************************************************************/ + +pascal void TruncPString(StringPtr destination, + ConstStr255Param source, + short maxLength) +{ + short charType; + + if ( source != NULL && destination != NULL ) /* don't do anything stupid */ + { + if ( source[0] > maxLength ) + { + /* Make sure the string isn't truncated in the middle of */ + /* a multi-byte character. */ + while (maxLength != 0) + { + // Note: CharacterByteType's textOffset parameter is zero-based from the textPtr parameter + charType = CharacterByteType((Ptr)&source[1], maxLength - 1, smSystemScript); + if ( (charType == smSingleByte) || (charType == smLastByte) ) + break; /* source[maxLength] is now a valid last character */ + --maxLength; + } + } + else + { + maxLength = source[0]; + } + /* Set the destination string length */ + destination[0] = maxLength; + /* and copy maxLength characters (if needed) */ + if ( source != destination ) + { + while ( maxLength != 0 ) + { + destination[maxLength] = source[maxLength]; + --maxLength; + } + } + } +} + +/*****************************************************************************/ + +static pascal Ptr GetTempBuffer(long buffReqSize, + long *buffActSize) +{ + enum + { + kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */ + }; + Ptr tempPtr; + + /* Make request a multiple of 1024 bytes */ + buffReqSize = buffReqSize & 0xfffffc00; + + if ( buffReqSize < 0x00000400 ) + { + /* Request was smaller than 1024 bytes - make it 1024 */ + buffReqSize = 0x00000400; + } + + /* Attempt to allocate the memory */ + tempPtr = NewPtr(buffReqSize); + + /* If request failed, go to backup plan */ + if ( (tempPtr == NULL) && (buffReqSize > 0x00000400) ) + { + /* + ** Try to get largest 1024-byte block available + ** leaving some slop for the toolbox if possible + */ + long freeMemory = (FreeMem() - kSlopMemory) & 0xfffffc00; + + buffReqSize = MaxBlock() & 0xfffffc00; + + if ( buffReqSize > freeMemory ) + { + buffReqSize = freeMemory; + } + + if ( buffReqSize == 0 ) + { + buffReqSize = 0x00000400; + } + + tempPtr = NewPtr(buffReqSize); + } + + /* Return bytes allocated */ + if ( tempPtr != NULL ) + { + *buffActSize = buffReqSize; + } + else + { + *buffActSize = 0; + } + + return ( tempPtr ); +} + +/*****************************************************************************/ + +/* +** GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync +** in cases where the returned volume name is not needed by the caller. +** The pathname and vRefNum parameters are not touched, and the pb +** parameter is initialized by PBHGetVInfoSync except that ioNamePtr in +** the parameter block is always returned as NULL (since it might point +** to the local tempPathname). +** +** I noticed using this code in several places, so here it is once. +** This reduces the code size of MoreFiles. +*/ +pascal OSErr GetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + HParmBlkPtr pb) +{ + Str255 tempPathname; + OSErr error; + + /* Make sure pb parameter is not NULL */ + if ( pb != NULL ) + { + pb->volumeParam.ioVRefNum = vRefNum; + if ( pathname == NULL ) + { + pb->volumeParam.ioNamePtr = NULL; + pb->volumeParam.ioVolIndex = 0; /* use ioVRefNum only */ + } + else + { + BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */ + pb->volumeParam.ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */ + pb->volumeParam.ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */ + } + error = PBHGetVInfoSync(pb); + pb->volumeParam.ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */ + } + else + { + error = paramErr; + } + return ( error ); +} + +/*****************************************************************************/ + +/* +** XGetVolumeInfoNoName uses pathname and vRefNum to call PBXGetVolInfoSync +** in cases where the returned volume name is not needed by the caller. +** The pathname and vRefNum parameters are not touched, and the pb +** parameter is initialized by PBXGetVolInfoSync except that ioNamePtr in +** the parameter block is always returned as NULL (since it might point +** to the local tempPathname). +*/ +pascal OSErr XGetVolumeInfoNoName(ConstStr255Param pathname, + short vRefNum, + XVolumeParamPtr pb) +{ + Str255 tempPathname; + OSErr error; + + /* Make sure pb parameter is not NULL */ + if ( pb != NULL ) + { + pb->ioVRefNum = vRefNum; + pb->ioXVersion = 0; /* this XVolumeParam version (0) */ + if ( pathname == NULL ) + { + pb->ioNamePtr = NULL; + pb->ioVolIndex = 0; /* use ioVRefNum only */ + } + else + { + BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */ + pb->ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */ + pb->ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */ + } + + { +#if !TARGET_API_MAC_CARBON + long response; + + /* Is PBXGetVolInfo available? */ + if ( ( Gestalt(gestaltFSAttr, &response) != noErr ) || ((response & (1L << gestaltFSSupports2TBVols)) == 0) ) + { + /* No, fall back on PBHGetVInfo */ + error = PBHGetVInfoSync((HParmBlkPtr)pb); + if ( error == noErr ) + { + /* calculate the ioVTotalBytes and ioVFreeBytes fields */ + pb->ioVTotalBytes = U64Multiply(U64SetU(pb->ioVNmAlBlks), U64SetU(pb->ioVAlBlkSiz)); + pb->ioVFreeBytes = U64Multiply(U64SetU(pb->ioVFrBlk), U64SetU(pb->ioVAlBlkSiz)); + } + } + else +#endif + { + /* Yes, so use it */ + error = CallPBXGetVolInfoSync(pb); + } + } + pb->ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */ + } + else + { + error = paramErr; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetCatInfoNoName(short vRefNum, + long dirID, + ConstStr255Param name, + CInfoPBPtr pb) +{ + Str31 tempName; + OSErr error; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb->dirInfo.ioNamePtr = tempName; + pb->dirInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb->dirInfo.ioNamePtr = (StringPtr)name; + pb->dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb->dirInfo.ioVRefNum = vRefNum; + pb->dirInfo.ioDrDirID = dirID; + error = PBGetCatInfoSync(pb); + pb->dirInfo.ioNamePtr = NULL; + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DetermineVRefNum(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + *realVRefNum = pb.volumeParam.ioVRefNum; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr HGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + unsigned long *freeBytes, + unsigned long *totalBytes) +{ + OSErr result; + UInt64 freeBytes64; + UInt64 totalBytes64; + + // get the best values possible from XGetVInfo + result = XGetVInfo(volReference, volName, vRefNum, &freeBytes64, &totalBytes64); + if ( result == noErr ) + { + // and pin those values if needed + if ( (UInt64ToUnsignedWide(freeBytes64)).hi != 0 ) + { + // pin to maximum 512-byte block aligned value + *freeBytes = 0xfffffe00; + } + else + { + *freeBytes = U32SetU(freeBytes64); + } + + if ( (UInt64ToUnsignedWide(totalBytes64)).hi != 0 ) + { + // pin to maximum 512-byte block aligned value + *totalBytes = 0xfffffe00; + } + else + { + *totalBytes = U32SetU(totalBytes64); + } + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr XGetVInfo(short volReference, + StringPtr volName, + short *vRefNum, + UInt64 *freeBytes, + UInt64 *totalBytes) +{ + OSErr result; + XVolumeParam pb; + +#if !TARGET_API_MAC_CARBON + + long response; + +#endif // !TARGET_API_MAC_CARBON + + pb.ioVRefNum = volReference; + pb.ioNamePtr = volName; + pb.ioXVersion = 0; /* this XVolumeParam version (0) */ + pb.ioVolIndex = 0; /* use ioVRefNum only, return volume name */ + +#if !TARGET_API_MAC_CARBON + + /* See if large volume support is available */ + if ( ( Gestalt(gestaltFSAttr, &response) == noErr ) && ((response & (1L << gestaltFSSupports2TBVols)) != 0) ) + +#endif // !TARGET_API_MAC_CARBON + + { + /* Large volume support is available */ + result = CallPBXGetVolInfoSync(&pb); + if ( result == noErr ) + { + /* The volume name was returned in volName (if not NULL) and */ + /* we have the volume's vRefNum and allocation block size */ + *vRefNum = pb.ioVRefNum; + + /* return the freeBytes and totalBytes */ + *totalBytes = pb.ioVTotalBytes; + *freeBytes = pb.ioVFreeBytes; + } + } + +#if !TARGET_API_MAC_CARBON + + else + { + /* No large volume support */ + /* Use PBHGetVInfoSync to get the results */ + result = PBHGetVInfoSync((HParmBlkPtr)&pb); + if ( result == noErr ) + { + VCB *theVCB; + + /* The volume name was returned in volName (if not NULL) and */ + /* we have the volume's vRefNum */ + *vRefNum = pb.ioVRefNum; + + /* System 7.5 (and beyond) pins the number of allocation blocks and */ + /* the number of free allocation blocks returned by PBHGetVInfo to */ + /* a value so that when multiplied by the allocation block size, */ + /* the volume will look like it has $7fffffff bytes or less. This */ + /* was done so older applications that use signed math or that use */ + /* the GetVInfo function (which uses signed math) will continue to work. */ + /* However, the unpinned numbers (which we want) are always available */ + /* in the volume's VCB so we'll get those values from the VCB. */ + /* Note: Carbon doesn't support the VCB queue, so this code cannot be */ + /* used (and is conditionalized out) by Carbon applications. */ + + /* Find the volume's VCB */ + theVCB = (VCB *)(GetVCBQHdr()->qHead); + while ( theVCB != NULL ) + { + if ( theVCB->vcbVRefNum == *vRefNum ) + { + break; + } + + theVCB = (VCB *)(theVCB->qLink); /* next VCB */ + } + + if ( theVCB != NULL ) + { + /* Found a VCB we can use. Get the un-pinned number of allocation blocks */ + /* and the number of free blocks from the VCB. */ + *freeBytes = U64Multiply(U64SetU((unsigned short)theVCB->vcbFreeBks), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + *totalBytes = U64Multiply(U64SetU((unsigned short)theVCB->vcbNmAlBlks), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + } + else + { + /* Didn't find a VCB we can use. Return the number of allocation blocks */ + /* and the number of free blocks returned by PBHGetVInfoSync. */ + *freeBytes = U64Multiply(U64SetU((unsigned short)pb.ioVFrBlk), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + *totalBytes = U64Multiply(U64SetU((unsigned short)pb.ioVNmAlBlks), U64SetU((unsigned long)pb.ioVAlBlkSiz)); + } + + } + } + +#endif // !TARGET_API_MAC_CARBON + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr CheckVolLock(ConstStr255Param pathname, + short vRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + if ( (pb.volumeParam.ioVAtrb & kHFSVolumeHardwareLockMask) != 0 ) + { + error = wPrErr; /* volume locked by hardware */ + } + else if ( (pb.volumeParam.ioVAtrb & kHFSVolumeSoftwareLockMask) != 0 ) + { + error = vLckdErr; /* volume locked by software */ + } + } + + return ( error ); +} + +/*****************************************************************************/ +// +// The following routines call Mac OS routines that are not supported by +// Carbon: +// +// GetDriverName +// FindDrive +// GetDiskBlocks +// GetVolState + +#if !TARGET_API_MAC_CARBON // { + + /*****************************************************************************/ + + pascal OSErr GetDriverName(short driverRefNum, + Str255 driverName) + { + OSErr result; + DCtlHandle theDctl; + DRVRHeaderPtr dHeaderPtr; + + theDctl = GetDCtlEntry(driverRefNum); + if ( theDctl != NULL ) + { + if ( (**theDctl).dCtlFlags & dRAMBasedMask ) + { + /* dctlDriver is handle - dereference */ + dHeaderPtr = *((DRVRHeaderHandle)(**theDctl).dCtlDriver); + } + else + { + /* dctlDriver is pointer */ + dHeaderPtr = (DRVRHeaderPtr)(**theDctl).dCtlDriver; + } + BlockMoveData((*dHeaderPtr).drvrName, driverName, (*dHeaderPtr).drvrName[0] + 1); + result = noErr; + } + else + { + driverName[0] = 0; + result = badUnitErr; /* bad reference number */ + } + + return ( result ); + } + + /*****************************************************************************/ + + pascal OSErr FindDrive(ConstStr255Param pathname, + short vRefNum, + DrvQElPtr *driveQElementPtr) + { + OSErr result; + HParamBlockRec hPB; + short driveNumber; + + *driveQElementPtr = NULL; + + /* First, use GetVolumeInfoNoName to determine the volume */ + result = GetVolumeInfoNoName(pathname, vRefNum, &hPB); + if ( result == noErr ) + { + /* + ** The volume can be either online, offline, or ejected. What we find in + ** ioVDrvInfo and ioVDRefNum will tell us which it is. + ** See Inside Macintosh: Files page 2-80 and the Technical Note + ** "FL 34 - VCBs and Drive Numbers : The Real Story" + ** Where we get the drive number depends on the state of the volume. + */ + if ( hPB.volumeParam.ioVDrvInfo != 0 ) + { + /* The volume is online and not ejected */ + /* Get the drive number */ + driveNumber = hPB.volumeParam.ioVDrvInfo; + } + else + { + /* The volume's is either offline or ejected */ + /* in either case, the volume is NOT online */ + + /* Is it ejected or just offline? */ + if ( hPB.volumeParam.ioVDRefNum > 0 ) + { + /* It's ejected, the drive number is ioVDRefNum */ + driveNumber = hPB.volumeParam.ioVDRefNum; + } + else + { + /* It's offline, the drive number is the negative of ioVDRefNum */ + driveNumber = (short)-hPB.volumeParam.ioVDRefNum; + } + } + + /* Get pointer to first element in drive queue */ + *driveQElementPtr = (DrvQElPtr)(GetDrvQHdr()->qHead); + + /* Search for a matching drive number */ + while ( (*driveQElementPtr != NULL) && ((*driveQElementPtr)->dQDrive != driveNumber) ) + { + *driveQElementPtr = (DrvQElPtr)(*driveQElementPtr)->qLink; + } + + if ( *driveQElementPtr == NULL ) + { + /* This should never happen since every volume must have a drive, but... */ + result = nsDrvErr; + } + } + + return ( result ); + } + + /*****************************************************************************/ + + pascal OSErr GetDiskBlocks(ConstStr255Param pathname, + short vRefNum, + unsigned long *numBlocks) + { + /* Various constants for GetDiskBlocks() */ + enum + { + /* return format list status code */ + kFmtLstCode = 6, + + /* reference number of .SONY driver */ + kSonyRefNum = 0xfffb, + + /* values returned by DriveStatus in DrvSts.twoSideFmt */ + kSingleSided = 0, + kDoubleSided = -1, + kSingleSidedSize = 800, /* 400K */ + kDoubleSidedSize = 1600, /* 800K */ + + /* values in DrvQEl.qType */ + kWordDrvSiz = 0, + kLongDrvSiz = 1, + + /* more than enough formatListRecords */ + kMaxFormatListRecs = 16 + }; + + DrvQElPtr driveQElementPtr; + unsigned long blocks; + ParamBlockRec pb; + FormatListRec formatListRecords[kMaxFormatListRecs]; + DrvSts status; + short formatListRecIndex; + OSErr result; + + blocks = 0; + + /* Find the drive queue element for this volume */ + result = FindDrive(pathname, vRefNum, &driveQElementPtr); + + /* + ** Make sure this is a real driver (dQRefNum < 0). + ** AOCE's Mail Enclosures volume uses 0 for dQRefNum which will cause + ** problems if you try to use it as a driver refNum. + */ + if ( (result == noErr) && (driveQElementPtr->dQRefNum >= 0) ) + { + result = paramErr; + } + else + { + /* Attempt to get the drive's format list. */ + /* (see the Technical Note "What Your Sony Drives For You") */ + + pb.cntrlParam.ioVRefNum = driveQElementPtr->dQDrive; + pb.cntrlParam.ioCRefNum = driveQElementPtr->dQRefNum; + pb.cntrlParam.csCode = kFmtLstCode; + pb.cntrlParam.csParam[0] = kMaxFormatListRecs; + *(long *)&pb.cntrlParam.csParam[1] = (long)&formatListRecords[0]; + + result = PBStatusSync(&pb); + + if ( result == noErr ) + { + /* The drive supports ReturnFormatList status call. */ + + /* Get the current disk's size. */ + for( formatListRecIndex = 0; + formatListRecIndex < pb.cntrlParam.csParam[0]; + ++formatListRecIndex ) + { + if ( (formatListRecords[formatListRecIndex].formatFlags & + diCIFmtFlagsCurrentMask) != 0 ) + { + blocks = formatListRecords[formatListRecIndex].volSize; + } + } + if ( blocks == 0 ) + { + /* This should never happen */ + result = paramErr; + } + } + else if ( driveQElementPtr->dQRefNum == (short)kSonyRefNum ) + { + /* The drive is a non-SuperDrive floppy which only supports 400K and 800K disks */ + + result = DriveStatus(driveQElementPtr->dQDrive, &status); + if ( result == noErr ) + { + switch ( status.twoSideFmt ) + { + case kSingleSided: + blocks = kSingleSidedSize; + break; + case kDoubleSided: + blocks = kDoubleSidedSize; + break; + default: + /* This should never happen */ + result = paramErr; + break; + } + } + } + else + { + /* The drive is not a floppy and it doesn't support ReturnFormatList */ + /* so use the dQDrvSz field(s) */ + + result = noErr; /* reset result */ + switch ( driveQElementPtr->qType ) + { + case kWordDrvSiz: + blocks = driveQElementPtr->dQDrvSz; + break; + case kLongDrvSiz: + blocks = ((unsigned long)driveQElementPtr->dQDrvSz2 << 16) + + driveQElementPtr->dQDrvSz; + break; + default: + /* This should never happen */ + result = paramErr; + break; + } + } + } + + if ( result == noErr ) + { + *numBlocks = blocks; + } + + return ( result ); + } + + /*****************************************************************************/ + + pascal OSErr GetVolState(ConstStr255Param pathname, + short vRefNum, + Boolean *volumeOnline, + Boolean *volumeEjected, + Boolean *driveEjectable, + Boolean *driverWantsEject) + { + HParamBlockRec pb; + short driveNumber; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + if ( pb.volumeParam.ioVDrvInfo != 0 ) + { + /* the volume is online and not ejected */ + *volumeOnline = true; + *volumeEjected = false; + + /* Get the drive number */ + driveNumber = pb.volumeParam.ioVDrvInfo; + } + else + { + /* the volume's is either offline or ejected */ + /* in either case, the volume is NOT online */ + *volumeOnline = false; + + /* Is it ejected? */ + *volumeEjected = pb.volumeParam.ioVDRefNum > 0; + + if ( *volumeEjected ) + { + /* If ejected, the drive number is ioVDRefNum */ + driveNumber = pb.volumeParam.ioVDRefNum; + } + else + { + /* If offline, the drive number is the negative of ioVDRefNum */ + driveNumber = (short)-pb.volumeParam.ioVDRefNum; + } + } + + { + DrvQElPtr drvQElem; + + /* Find the drive queue element by searching the drive queue */ + drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead); + while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNumber) ) + { + drvQElem = (DrvQElPtr)drvQElem->qLink; + } + + if ( drvQElem != NULL ) + { + /* + ** Each drive queue element is preceded by 4 flag bytes. + ** Byte 1 (the second flag byte) has bits that tell us if a + ** drive is ejectable and if its driver wants an eject call. + ** See Inside Macintosh: Files, page 2-85. + */ + { + Ptr flagBytePtr; + + /* point to byte 1 of the flag bytes */ + flagBytePtr = (Ptr)drvQElem; + flagBytePtr -= 3; + + /* + ** The drive is ejectable if flag byte 1 does not contain + ** 0x08 (nonejectable) or 0x48 (nonejectable, but wants eject call). + */ + + *driveEjectable = (*flagBytePtr != 0x08) && (*flagBytePtr != 0x48); + + /* + ** The driver wants an eject call if flag byte 1 does not contain + ** 0x08 (nonejectable). This may seem like a minor point, but some + ** disk drivers use the Eject request to flush their caches to disk + ** and you wouldn't want to skip that step after unmounting a volume. + */ + + *driverWantsEject = (*flagBytePtr != 0x08); + } + } + else + { + /* Didn't find the drive (this should never happen) */ + *driveEjectable = false; + *driverWantsEject = false; + } + } + } + + return ( error ); + } + + /*****************************************************************************/ + +#endif // } !TARGET_API_MAC_CARBON + +/*****************************************************************************/ + +pascal OSErr GetVolFileSystemID(ConstStr255Param pathname, + short vRefNum, + short *fileSystemID) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname,vRefNum, &pb); + if ( error == noErr ) + { + *fileSystemID = pb.volumeParam.ioVFSID; + } + + return ( error ); +} + +/*****************************************************************************/ + +// +// Note: Under Carbon there are no drive numbers, so you cannot call +// Eject with a drive number after unmounting a volume. +// When a Carbon application calls UnmountVol, CarbonLib will make +// sure ejectable media is ejected (leaving ejectable media in the +// disk drive makes no sense to Carbon applications). +// +pascal OSErr UnmountAndEject(ConstStr255Param pathname, + short vRefNum) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname, vRefNum, &pb); + if ( error == noErr ) + { + +#if !TARGET_API_MAC_CARBON + + short driveNum; + Boolean ejected, wantsEject; + DrvQElPtr drvQElem; + + if ( pb.volumeParam.ioVDrvInfo != 0 ) + { + /* the volume is online and not ejected */ + ejected = false; + + /* Get the drive number */ + driveNum = pb.volumeParam.ioVDrvInfo; + } + else + { + /* the volume is ejected or offline */ + + /* Is it ejected? */ + ejected = pb.volumeParam.ioVDRefNum > 0; + + if ( ejected ) + { + /* If ejected, the drive number is ioVDRefNum */ + driveNum = pb.volumeParam.ioVDRefNum; + } + else + { + /* If offline, the drive number is the negative of ioVDRefNum */ + driveNum = (short)-pb.volumeParam.ioVDRefNum; + } + } + + /* find the drive queue element */ + drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead); + while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNum) ) + { + drvQElem = (DrvQElPtr)drvQElem->qLink; + } + + if ( drvQElem != NULL ) + { + /* does the drive want an eject call */ + wantsEject = (*((Ptr)((Ptr)drvQElem - 3)) != 8); + } + else + { + /* didn't find the drive!! */ + wantsEject = false; + } + +#endif // !TARGET_API_MAC_CARBON + + /* unmount the volume */ + pb.volumeParam.ioNamePtr = NULL; + /* ioVRefNum is already filled in from PBHGetVInfo */ + error = PBUnmountVol((ParmBlkPtr)&pb); + +#if !TARGET_API_MAC_CARBON + + if ( error == noErr ) + { + if ( wantsEject && !ejected ) + { + /* eject the media from the drive if needed */ + pb.volumeParam.ioVRefNum = driveNum; + error = PBEject((ParmBlkPtr)&pb); + } + } + +#endif // !TARGET_API_MAC_CARBON + + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr OnLine(FSSpecPtr volumes, + short reqVolCount, + short *actVolCount, + short *volIndex) +{ + HParamBlockRec pb; + OSErr error = noErr; + FSSpec *endVolArray; + + if ( *volIndex > 0 ) + { + *actVolCount = 0; + for ( endVolArray = volumes + reqVolCount; (volumes < endVolArray) && (error == noErr); ++volumes ) + { + pb.volumeParam.ioNamePtr = (StringPtr) & volumes->name; + pb.volumeParam.ioVolIndex = *volIndex; + error = PBHGetVInfoSync(&pb); + if ( error == noErr ) + { + volumes->parID = fsRtParID; /* the root directory's parent is 1 */ + volumes->vRefNum = pb.volumeParam.ioVRefNum; + ++*volIndex; + ++*actVolCount; + } + } + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr SetDefault(short newVRefNum, + long newDirID, + short *oldVRefNum, + long *oldDirID) +{ + OSErr error; + + /* Get the current default volume/directory. */ + error = HGetVol(NULL, oldVRefNum, oldDirID); + if ( error == noErr ) + { + /* Set the new default volume/directory */ + error = HSetVol(NULL, newVRefNum, newDirID); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RestoreDefault(short oldVRefNum, + long oldDirID) +{ + OSErr error; + +#if !TARGET_API_MAC_CARBON + + short defaultVRefNum; + long defaultDirID; + long defaultProcID; + + /* Determine if the default volume was a wdRefNum. */ + error = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID); + if ( error == noErr ) + { + /* Restore the old default volume/directory, one way or the other. */ + if ( defaultDirID != fsRtDirID ) + { + /* oldVRefNum was a wdRefNum - use SetVol */ + error = SetVol(NULL, oldVRefNum); + } + else + { + +#endif // !TARGET_API_MAC_CARBON + + /* oldVRefNum was a real vRefNum - use HSetVol */ + error = HSetVol(NULL, oldVRefNum, oldDirID); + +#if !TARGET_API_MAC_CARBON + + } + } +#endif // !TARGET_API_MAC_CARBON + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + DInfo *fndrInfo) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + if ( (pb.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* it's a directory, return the DInfo */ + *fndrInfo = pb.dirInfo.ioDrUsrWds; + } + else + { + /* oops, a file was passed */ + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDInfo(const FSSpec *spec, + DInfo *fndrInfo) +{ + return ( GetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) ); +} + +/*****************************************************************************/ + +pascal OSErr SetDInfo(short vRefNum, + long dirID, + ConstStr255Param name, + const DInfo *fndrInfo) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.dirInfo.ioNamePtr = (StringPtr)name; + pb.dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* it's a directory, set the DInfo */ + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + } + else + { + pb.dirInfo.ioDrDirID = dirID; + } + pb.dirInfo.ioDrUsrWds = *fndrInfo; + error = PBSetCatInfoSync(&pb); + } + else + { + /* oops, a file was passed */ + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetDInfo(const FSSpec *spec, + const DInfo *fndrInfo) +{ + return ( SetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirectoryID(short vRefNum, + long dirID, + ConstStr255Param name, + long *theDirID, + Boolean *isDirectory) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + *isDirectory = (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0; + if ( *isDirectory ) + { + *theDirID = pb.dirInfo.ioDrDirID; + } + else + { + *theDirID = pb.hFileInfo.ioFlParID; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetDirectoryID(const FSSpec *spec, + long *theDirID, + Boolean *isDirectory) +{ + return ( GetDirectoryID(spec->vRefNum, spec->parID, spec->name, + theDirID, isDirectory) ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirName(short vRefNum, + long dirID, + Str31 name) +{ + CInfoPBRec pb; + OSErr error; + + if ( name != NULL ) + { + pb.dirInfo.ioNamePtr = name; + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetIOACUser(short vRefNum, + long dirID, + ConstStr255Param name, + SInt8 *ioACUser) +{ + CInfoPBRec pb; + OSErr error; + + /* Clear ioACUser before calling PBGetCatInfo since some file systems + ** don't bother to set or clear this field. If ioACUser isn't set by the + ** file system, then you'll get the zero value back (full access) which + ** is the access you have on volumes that don't support ioACUser. + */ + pb.dirInfo.ioACUser = 0; /* ioACUser used to be filler2 */ + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) == 0 ) + { + /* oops, a file was passed */ + error = dirNFErr; + } + else + { + *ioACUser = pb.dirInfo.ioACUser; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetIOACUser(const FSSpec *spec, + SInt8 *ioACUser) +{ + return ( GetIOACUser(spec->vRefNum, spec->parID, spec->name, ioACUser) ); +} + +/*****************************************************************************/ + +pascal OSErr GetParentID(short vRefNum, + long dirID, + ConstStr255Param name, + long *parID) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + short realVRefNum; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + /* + ** There's a bug in HFS where the wrong parent dir ID can be + ** returned if multiple separators are used at the end of a + ** pathname. For example, if the pathname: + ** 'volumeName:System Folder:Extensions::' + ** is passed, the directory ID of the Extensions folder is + ** returned in the ioFlParID field instead of fsRtDirID. Since + ** multiple separators at the end of a pathname always specifies + ** a directory, we only need to work-around cases where the + ** object is a directory and there are multiple separators at + ** the end of the name parameter. + */ + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* Its a directory */ + + /* is there a pathname? */ + if ( pb.hFileInfo.ioNamePtr == name ) + { + /* could it contain multiple separators? */ + if ( name[0] >= 2 ) + { + /* does it contain multiple separators at the end? */ + if ( (name[name[0]] == ':') && (name[name[0] - 1] == ':') ) + { + /* OK, then do the extra stuff to get the correct parID */ + + /* Get the real vRefNum (this should not fail) */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + /* we don't need the parent's name, but add protect against File Sharing problem */ + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioVRefNum = realVRefNum; + /* pb.dirInfo.ioDrDirID already contains the */ + /* dirID of the directory object */ + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + /* now, pb.dirInfo.ioDrParID contains the correct parID */ + } + } + } + } + } + + if ( error == noErr ) + { + /* if no errors, then pb.hFileInfo.ioFlParID (pb.dirInfo.ioDrParID) */ + /* contains the parent ID */ + *parID = pb.hFileInfo.ioFlParID; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetFilenameFromPathname(ConstStr255Param pathname, + Str255 filename) +{ + short index; + short nameEnd; + OSErr error; + + /* default to no filename */ + filename[0] = 0; + + /* check for no pathname */ + if ( pathname != NULL ) + { + /* get string length */ + index = pathname[0]; + + /* check for empty string */ + if ( index != 0 ) + { + /* skip over last trailing colon (if any) */ + if ( pathname[index] == ':' ) + { + --index; + } + + /* save the end of the string */ + nameEnd = index; + + /* if pathname ends with multiple colons, then this pathname refers */ + /* to a directory, not a file */ + if ( pathname[index] != ':' ) + { + /* parse backwards until we find a colon or hit the beginning of the pathname */ + while ( (index != 0) && (pathname[index] != ':') ) + { + --index; + } + + /* if we parsed to the beginning of the pathname and the pathname ended */ + /* with a colon, then pathname is a full pathname to a volume, not a file */ + if ( (index != 0) || (pathname[pathname[0]] != ':') ) + { + /* get the filename and return noErr */ + filename[0] = (char)(nameEnd - index); + BlockMoveData(&pathname[index+1], &filename[1], nameEnd - index); + error = noErr; + } + else + { + /* pathname to a volume, not a file */ + error = notAFileErr; + } + } + else + { + /* directory, not a file */ + error = notAFileErr; + } + } + else + { + /* empty string isn't a file */ + error = notAFileErr; + } + } + else + { + /* NULL pathname isn't a file */ + error = notAFileErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetObjectLocation(short vRefNum, + long dirID, + ConstStr255Param pathname, + short *realVRefNum, + long *realParID, + Str255 realName, + Boolean *isDirectory) +{ + OSErr error; + CInfoPBRec pb; + Str255 tempPathname; + + /* clear results */ + *realVRefNum = 0; + *realParID = 0; + realName[0] = 0; + + /* + ** Get the real vRefNum + */ + error = DetermineVRefNum(pathname, vRefNum, realVRefNum); + if ( error == noErr ) + { + /* + ** Determine if the object already exists and if so, + ** get the real parent directory ID if it's a file + */ + + /* Protection against File Sharing problem */ + if ( (pathname == NULL) || (pathname[0] == 0) ) + { + tempPathname[0] = 0; + pb.hFileInfo.ioNamePtr = tempPathname; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)pathname; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + /* + ** The file system object is present and we have the file's real parID + */ + + /* Is it a directory or a file? */ + *isDirectory = (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0; + if ( *isDirectory ) + { + /* + ** It's a directory, get its name and parent dirID, and then we're done + */ + + pb.dirInfo.ioNamePtr = realName; + pb.dirInfo.ioVRefNum = *realVRefNum; + /* pb.dirInfo.ioDrDirID already contains the dirID of the directory object */ + pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */ + error = PBGetCatInfoSync(&pb); + + /* get the parent ID here, because the file system can return the */ + /* wrong parent ID from the last call. */ + *realParID = pb.dirInfo.ioDrParID; + } + else + { + /* + ** It's a file - use the parent directory ID from the last call + ** to GetCatInfoparse, get the file name, and then we're done + */ + *realParID = pb.hFileInfo.ioFlParID; + error = GetFilenameFromPathname(pathname, realName); + } + } + else if ( error == fnfErr ) + { + /* + ** The file system object is not present - see if its parent is present + */ + + /* + ** Parse to get the object name from end of pathname + */ + error = GetFilenameFromPathname(pathname, realName); + + /* if we can't get the object name from the end, we can't continue */ + if ( error == noErr ) + { + /* + ** What we want now is the pathname minus the object name + ** for example: + ** if pathname is 'vol:dir:file' tempPathname becomes 'vol:dir:' + ** if pathname is 'vol:dir:file:' tempPathname becomes 'vol:dir:' + ** if pathname is ':dir:file' tempPathname becomes ':dir:' + ** if pathname is ':dir:file:' tempPathname becomes ':dir:' + ** if pathname is ':file' tempPathname becomes ':' + ** if pathname is 'file or file:' tempPathname becomes '' + */ + + /* get a copy of the pathname */ + BlockMoveData(pathname, tempPathname, pathname[0] + 1); + + /* remove the object name */ + tempPathname[0] -= realName[0]; + /* and the trailing colon (if any) */ + if ( pathname[pathname[0]] == ':' ) + { + --tempPathname[0]; + } + + /* OK, now get the parent's directory ID */ + + /* Protection against File Sharing problem */ + pb.hFileInfo.ioNamePtr = (StringPtr)tempPathname; + if ( tempPathname[0] != 0 ) + { + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + else + { + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + *realParID = pb.dirInfo.ioDrDirID; + + *isDirectory = false; /* we don't know what the object is really going to be */ + } + + if ( error != noErr ) + { + error = dirNFErr; /* couldn't find parent directory */ + } + else + { + error = fnfErr; /* we found the parent, but not the file */ + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetDirItems(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean getFiles, + Boolean getDirectories, + FSSpecPtr items, + short reqItemCount, + short *actItemCount, + short *itemIndex) /* start with 1, then use what's returned */ +{ + CInfoPBRec pb; + OSErr error; + long theDirID; + Boolean isDirectory; + FSSpec *endItemsArray; + + if ( *itemIndex > 0 ) + { + /* NOTE: If I could be sure that the caller passed a real vRefNum and real directory */ + /* to this routine, I could rip out calls to DetermineVRefNum and GetDirectoryID and this */ + /* routine would be much faster because of the overhead of DetermineVRefNum and */ + /* GetDirectoryID and because GetDirectoryID blows away the directory index hint the Macintosh */ + /* file system keeps for indexed calls. I can't be sure, so for maximum throughput, */ + /* pass a big array of FSSpecs so you can get the directory's contents with few calls */ + /* to this routine. */ + + /* get the real volume reference number */ + error = DetermineVRefNum(name, vRefNum, &pb.hFileInfo.ioVRefNum); + if ( error == noErr ) + { + /* and the real directory ID of this directory (and make sure it IS a directory) */ + error = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory); + if ( error == noErr ) + { + if ( isDirectory ) + { + *actItemCount = 0; + endItemsArray = items + reqItemCount; + while ( (items < endItemsArray) && (error == noErr) ) + { + pb.hFileInfo.ioNamePtr = (StringPtr) &items->name; + pb.hFileInfo.ioDirID = theDirID; + pb.hFileInfo.ioFDirIndex = *itemIndex; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + items->parID = pb.hFileInfo.ioFlParID; /* return item's parID */ + items->vRefNum = pb.hFileInfo.ioVRefNum; /* return item's vRefNum */ + ++*itemIndex; /* prepare to get next item in directory */ + + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + if ( getDirectories ) + { + ++*actItemCount; /* keep this item */ + ++items; /* point to next item */ + } + } + else + { + if ( getFiles ) + { + ++*actItemCount; /* keep this item */ + ++items; /* point to next item */ + } + } + } + } + } + else + { + /* it wasn't a directory */ + error = dirNFErr; + } + } + } + } + else + { + /* bad itemIndex */ + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +static void DeleteLevel(long dirToDelete, + DeleteEnumGlobalsPtr theGlobals) +{ + long savedDir; + + do + { + /* prepare to delete directory */ + theGlobals->myPB.ciPB.dirInfo.ioNamePtr = (StringPtr)&theGlobals->itemName; + theGlobals->myPB.ciPB.dirInfo.ioFDirIndex = 1; /* get first item */ + theGlobals->myPB.ciPB.dirInfo.ioDrDirID = dirToDelete; /* in this directory */ + theGlobals->error = PBGetCatInfoSync(&(theGlobals->myPB.ciPB)); + if ( theGlobals->error == noErr ) + { + savedDir = dirToDelete; + /* We have an item. Is it a file or directory? */ + if ( (theGlobals->myPB.ciPB.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* it's a directory */ + savedDir = theGlobals->myPB.ciPB.dirInfo.ioDrDirID; /* save dirID of directory instead */ + DeleteLevel(theGlobals->myPB.ciPB.dirInfo.ioDrDirID, theGlobals); /* Delete its contents */ + theGlobals->myPB.ciPB.dirInfo.ioNamePtr = NULL; /* prepare to delete directory */ + } + if ( theGlobals->error == noErr ) + { + theGlobals->myPB.ciPB.dirInfo.ioDrDirID = savedDir; /* restore dirID */ + theGlobals->myPB.hPB.fileParam.ioFVersNum = 0; /* just in case it's used on an MFS volume... */ + theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* delete this item */ + if ( theGlobals->error == fLckdErr ) + { + (void) PBHRstFLockSync(&(theGlobals->myPB.hPB)); /* unlock it */ + theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* and try again */ + } + } + } + } while ( theGlobals->error == noErr ); + + if ( theGlobals->error == fnfErr ) + { + theGlobals->error = noErr; + } +} + +/*****************************************************************************/ + +pascal OSErr DeleteDirectoryContents(short vRefNum, + long dirID, + ConstStr255Param name) +{ + DeleteEnumGlobals theGlobals; + Boolean isDirectory; + OSErr error; + + /* Get the real dirID and make sure it is a directory. */ + error = GetDirectoryID(vRefNum, dirID, name, &dirID, &isDirectory); + if ( error == noErr ) + { + if ( isDirectory ) + { + /* Get the real vRefNum */ + error = DetermineVRefNum(name, vRefNum, &vRefNum); + if ( error == noErr ) + { + /* Set up the globals we need to access from the recursive routine. */ + theGlobals.myPB.ciPB.dirInfo.ioVRefNum = vRefNum; + + /* Here we go into recursion land... */ + DeleteLevel(dirID, &theGlobals); + error = theGlobals.error; + } + } + else + { + error = dirNFErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr DeleteDirectory(short vRefNum, + long dirID, + ConstStr255Param name) +{ + OSErr error; + + /* Make sure a directory was specified and then delete its contents */ + error = DeleteDirectoryContents(vRefNum, dirID, name); + if ( error == noErr ) + { + error = HDelete(vRefNum, dirID, name); + if ( error == fLckdErr ) + { + (void) HRstFLock(vRefNum, dirID, name); /* unlock the directory locked by AppleShare */ + error = HDelete(vRefNum, dirID, name); /* and try again */ + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr CheckObjectLock(short vRefNum, + long dirID, + ConstStr255Param name) +{ + CInfoPBRec pb; + OSErr error; + + error = GetCatInfoNoName(vRefNum, dirID, name, &pb); + if ( error == noErr ) + { + /* check locked bit */ + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribLockedMask) != 0 ) + { + error = fLckdErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCheckObjectLock(const FSSpec *spec) +{ + return ( CheckObjectLock(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr GetFileSize(short vRefNum, + long dirID, + ConstStr255Param fileName, + long *dataSize, + long *rsrcSize) +{ + HParamBlockRec pb; + OSErr error; + + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioDirID = dirID; + pb.fileParam.ioFDirIndex = 0; + error = PBHGetFInfoSync(&pb); + if ( error == noErr ) + { + *dataSize = pb.fileParam.ioFlLgLen; + *rsrcSize = pb.fileParam.ioFlRLgLen; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFileSize(const FSSpec *spec, + long *dataSize, + long *rsrcSize) +{ + return ( GetFileSize(spec->vRefNum, spec->parID, spec->name, dataSize, rsrcSize) ); +} + +/*****************************************************************************/ + +pascal OSErr BumpDate(short vRefNum, + long dirID, + ConstStr255Param name) +/* Given a file or directory, change its modification date to the current date/time. */ +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + unsigned long secs; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + GetDateTime(&secs); + /* set mod date to current date, or one second into the future + if mod date = current date */ + pb.hFileInfo.ioFlMdDat = (secs == pb.hFileInfo.ioFlMdDat) ? (++secs) : (secs); + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID; + } + else + { + pb.hFileInfo.ioDirID = dirID; + } + error = PBSetCatInfoSync(&pb); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpBumpDate(const FSSpec *spec) +{ + return ( BumpDate(spec->vRefNum, spec->parID, spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr ChangeCreatorType(short vRefNum, + long dirID, + ConstStr255Param name, + OSType creator, + OSType fileType) +{ + CInfoPBRec pb; + OSErr error; + short realVRefNum; + long parID; + + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + if ( (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) == 0 ) /* if file */ + { + parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */ + + /* If creator not 0x00000000, change creator */ + if ( creator != (OSType)0x00000000 ) + { + pb.hFileInfo.ioFlFndrInfo.fdCreator = creator; + } + + /* If fileType not 0x00000000, change fileType */ + if ( fileType != (OSType)0x00000000 ) + { + pb.hFileInfo.ioFlFndrInfo.fdType = fileType; + } + + pb.hFileInfo.ioDirID = dirID; + error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */ + + if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */ + { + /* get the real vRefNum in case a full pathname was passed */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = BumpDate(realVRefNum, parID, NULL); + /* and bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made */ + } + } + } + else + { + /* it was a directory, not a file */ + error = notAFileErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpChangeCreatorType(const FSSpec *spec, + OSType creator, + OSType fileType) +{ + return ( ChangeCreatorType(spec->vRefNum, spec->parID, spec->name, creator, fileType) ); +} + +/*****************************************************************************/ + +pascal OSErr ChangeFDFlags(short vRefNum, + long dirID, + ConstStr255Param name, + Boolean setBits, + unsigned short flagBits) +{ + CInfoPBRec pb; + Str31 tempName; + OSErr error; + short realVRefNum; + long parID; + + /* Protection against File Sharing problem */ + if ( (name == NULL) || (name[0] == 0) ) + { + tempName[0] = 0; + pb.hFileInfo.ioNamePtr = tempName; + pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.hFileInfo.ioNamePtr = (StringPtr)name; + pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + pb.hFileInfo.ioVRefNum = vRefNum; + pb.hFileInfo.ioDirID = dirID; + error = PBGetCatInfoSync(&pb); + if ( error == noErr ) + { + parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */ + + /* set or clear the appropriate bits in the Finder flags */ + if ( setBits ) + { + /* OR in the bits */ + pb.hFileInfo.ioFlFndrInfo.fdFlags |= flagBits; + } + else + { + /* AND out the bits */ + pb.hFileInfo.ioFlFndrInfo.fdFlags &= ~flagBits; + } + + if ( pb.dirInfo.ioNamePtr == tempName ) + { + pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID; + } + else + { + pb.hFileInfo.ioDirID = dirID; + } + + error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */ + + if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */ + { + /* get the real vRefNum in case a full pathname was passed */ + error = DetermineVRefNum(name, vRefNum, &realVRefNum); + if ( error == noErr ) + { + error = BumpDate(realVRefNum, parID, NULL); + /* and bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made */ + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpChangeFDFlags(const FSSpec *spec, + Boolean setBits, + unsigned short flagBits) +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, setBits, flagBits) ); +} + +/*****************************************************************************/ + +pascal OSErr SetIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, make it invisible. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetIsInvisible(const FSSpec *spec) + /* Given a file or directory, make it invisible. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearIsInvisible(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, make it visible. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearIsInvisible(const FSSpec *spec) + /* Given a file or directory, make it visible. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +pascal OSErr SetNameLocked(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, lock its name. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetNameLocked(const FSSpec *spec) + /* Given a file or directory, lock its name. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearNameLocked(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, unlock its name. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearNameLocked(const FSSpec *spec) + /* Given a file or directory, unlock its name. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kNameLocked) ); +} + +/*****************************************************************************/ + +pascal OSErr SetIsStationery(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, make it a stationery pad. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetIsStationery(const FSSpec *spec) + /* Given a file, make it a stationery pad. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearIsStationery(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, clear the stationery bit. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearIsStationery(const FSSpec *spec) + /* Given a file, clear the stationery bit. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsStationery) ); +} + +/*****************************************************************************/ + +pascal OSErr SetHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, indicate that it has a custom icon. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, true, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpSetHasCustomIcon(const FSSpec *spec) + /* Given a file or directory, indicate that it has a custom icon. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearHasCustomIcon(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file or directory, indicate that it does not have a custom icon. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearHasCustomIcon(const FSSpec *spec) + /* Given a file or directory, indicate that it does not have a custom icon. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +pascal OSErr ClearHasBeenInited(short vRefNum, + long dirID, + ConstStr255Param name) + /* Given a file, clear its "has been inited" bit. */ +{ + return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +pascal OSErr FSpClearHasBeenInited(const FSSpec *spec) + /* Given a file, clear its "has been inited" bit. */ +{ + return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +pascal OSErr CopyFileMgrAttributes(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + Boolean copyLockBit) +{ + UniversalFMPB pb; + Str31 tempName; + OSErr error; + Boolean objectIsDirectory; + + pb.ciPB.hFileInfo.ioVRefNum = srcVRefNum; + pb.ciPB.hFileInfo.ioDirID = srcDirID; + + /* Protection against File Sharing problem */ + if ( (srcName == NULL) || (srcName[0] == 0) ) + { + tempName[0] = 0; + pb.ciPB.hFileInfo.ioNamePtr = tempName; + pb.ciPB.hFileInfo.ioFDirIndex = -1; /* use ioDirID */ + } + else + { + pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)srcName; + pb.ciPB.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */ + } + error = PBGetCatInfoSync(&pb.ciPB); + if ( error == noErr ) + { + objectIsDirectory = ( (pb.ciPB.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ); + pb.ciPB.hFileInfo.ioVRefNum = dstVRefNum; + pb.ciPB.hFileInfo.ioDirID = dstDirID; + if ( (dstName != NULL) && (dstName[0] == 0) ) + { + pb.ciPB.hFileInfo.ioNamePtr = NULL; + } + else + { + pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)dstName; + } + /* don't copy the hasBeenInited bit */ + pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags = ( pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags & ~kHasBeenInited ); + error = PBSetCatInfoSync(&pb.ciPB); + if ( (error == noErr) && (copyLockBit) && ((pb.ciPB.hFileInfo.ioFlAttrib & kioFlAttribLockedMask) != 0) ) + { + pb.hPB.fileParam.ioFVersNum = 0; + error = PBHSetFLockSync(&pb.hPB); + if ( (error != noErr) && (objectIsDirectory) ) + { + error = noErr; /* ignore lock errors if destination is directory */ + } + } + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyFileMgrAttributes(const FSSpec *srcSpec, + const FSSpec *dstSpec, + Boolean copyLockBit) +{ + return ( CopyFileMgrAttributes(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name, + copyLockBit) ); +} + +/*****************************************************************************/ + +pascal OSErr HOpenAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum) +{ + HParamBlockRec pb; + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize = sizeof(GetVolParmsInfoBuffer); + + pb.ioParam.ioMisc = NULL; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + + /* get volume attributes */ + /* this preflighting is needed because Foreign File Access based file systems don't */ + /* return the correct error result to the OpenDeny call */ + error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasOpenDeny(&volParmsInfo) ) + { + /* if volume supports OpenDeny, use it and return */ + pb.accessParam.ioDenyModes = denyModes; + error = PBHOpenDenySync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* OpenDeny isn't supported, so try File Manager Open functions */ + + /* If request includes write permission, then see if the volume is */ + /* locked by hardware or software. The HFS file system doesn't check */ + /* for this when a file is opened - you only find out later when you */ + /* try to write and the write fails with a wPrErr or a vLckdErr. */ + + if ( (denyModes & dmWr) != 0 ) + { + error = CheckVolLock(fileName, vRefNum); + } + else + { + error = noErr; + } + + if ( error == noErr ) + { + /* Set File Manager permissions to closest thing possible */ + if ( (denyModes == dmWr) || (denyModes == dmRdWr) ) + { + pb.ioParam.ioPermssn = fsRdWrShPerm; + } + else + { + pb.ioParam.ioPermssn = denyModes % 4; + } + + error = PBHOpenDFSync(&pb); /* Try OpenDF */ + if ( error == paramErr ) + { + error = PBHOpenSync(&pb); /* OpenDF not supported, so try Open */ + } + *refNum = pb.ioParam.ioRefNum; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenAware(const FSSpec *spec, + short denyModes, + short *refNum) +{ + return ( HOpenAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) ); +} + +/*****************************************************************************/ + +pascal OSErr HOpenRFAware(short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short *refNum) +{ + HParamBlockRec pb; + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize = sizeof(GetVolParmsInfoBuffer); + + pb.ioParam.ioMisc = NULL; + pb.fileParam.ioFVersNum = 0; + pb.fileParam.ioNamePtr = (StringPtr)fileName; + pb.fileParam.ioVRefNum = vRefNum; + pb.fileParam.ioDirID = dirID; + + /* get volume attributes */ + /* this preflighting is needed because Foreign File Access based file systems don't */ + /* return the correct error result to the OpenRFDeny call */ + error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasOpenDeny(&volParmsInfo) ) + { + /* if volume supports OpenRFDeny, use it and return */ + if ( hasOpenDeny(&volParmsInfo) ) + { + pb.accessParam.ioDenyModes = denyModes; + error = PBHOpenRFDenySync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* OpenRFDeny isn't supported, so try File Manager OpenRF function */ + + /* If request includes write permission, then see if the volume is */ + /* locked by hardware or software. The HFS file system doesn't check */ + /* for this when a file is opened - you only find out later when you */ + /* try to write and the write fails with a wPrErr or a vLckdErr. */ + + if ( (denyModes & dmWr) != 0 ) + { + error = CheckVolLock(fileName, vRefNum); + } + else + { + error = noErr; + } + + if ( error == noErr ) + { + /* Set File Manager permissions to closest thing possible */ + if ( (denyModes == dmWr) || (denyModes == dmRdWr) ) + { + pb.ioParam.ioPermssn = fsRdWrShPerm; + } + else + { + pb.ioParam.ioPermssn = denyModes % 4; + } + + error = PBHOpenRFSync(&pb); + *refNum = pb.ioParam.ioRefNum; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpOpenRFAware(const FSSpec *spec, + short denyModes, + short *refNum) +{ + return ( HOpenRFAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) ); +} + +/*****************************************************************************/ + +pascal OSErr FSReadNoCache(short refNum, + long *count, + void *buffPtr) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + noCacheMask; /* fsAtMark + noCacheMask */ + pb.ioParam.ioPosOffset = 0; + error = PBReadSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSWriteNoCache(short refNum, + long *count, + const void *buffPtr) +{ + ParamBlockRec pb; + OSErr error; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + noCacheMask; /* fsAtMark + noCacheMask */ + pb.ioParam.ioPosOffset = 0; + error = PBWriteSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( error ); +} + +/*****************************************************************************/ + +/* +** See if numBytes bytes of buffer1 are equal to buffer2. +*/ +static Boolean EqualMemory(const void *buffer1, const void *buffer2, unsigned long numBytes) +{ + register unsigned char *b1 = (unsigned char *)buffer1; + register unsigned char *b2 = (unsigned char *)buffer2; + + if ( b1 != b2 ) /* if buffer pointers are same, then they are equal */ + { + while ( numBytes > 0 ) + { + /* compare the bytes and then increment the pointers */ + if ( (*b1++ - *b2++) != 0 ) + { + return ( false ); + } + --numBytes; + } + } + + return ( true ); +} + +/*****************************************************************************/ + +/* +** Read any number of bytes from an open file using read-verify mode. +** The FSReadVerify function reads any number of bytes from an open file +** and verifies them against the data in the buffer pointed to by buffPtr. +** +** Because of a bug in the HFS file system, only non-block aligned parts of +** the read are verified against the buffer data and the rest is *copied* +** into the buffer. Thus, you shouldn't verify against your original data; +** instead, you should verify against a copy of the original data and then +** compare the read-verified copy against the original data after calling +** FSReadVerify. That's why this function isn't exported - it needs the +** wrapper provided by FSWriteVerify. +*/ +static OSErr FSReadVerify(short refNum, + long *count, + void *buffPtr) +{ + ParamBlockRec pb; + OSErr result; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = (Ptr)buffPtr; + pb.ioParam.ioReqCount = *count; + pb.ioParam.ioPosMode = fsAtMark + rdVerify; + pb.ioParam.ioPosOffset = 0; + result = PBReadSync(&pb); + *count = pb.ioParam.ioActCount; /* always return count */ + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr FSWriteVerify(short refNum, + long *count, + const void *buffPtr) +{ + Ptr verifyBuffer; + long position; + long bufferSize; + long byteCount; + long bytesVerified; + Ptr startVerify; + OSErr result; + + /* + ** Allocate the verify buffer + ** Try to get get a large enough buffer to verify in one pass. + ** If that fails, use GetTempBuffer to get a buffer. + */ + bufferSize = *count; + verifyBuffer = NewPtr(bufferSize); + if ( verifyBuffer == NULL ) + { + verifyBuffer = GetTempBuffer(bufferSize, &bufferSize); + } + if ( verifyBuffer != NULL ) + { + /* Save the current position */ + result = GetFPos(refNum, &position); + if ( result == noErr ) + { + /* Write the data */ + result = FSWrite(refNum, count, buffPtr); + if ( result == noErr ) + { + /* Restore the original position */ + result = SetFPos(refNum, fsFromStart, position); + if ( result == noErr ) + { + /* + ** *count = total number of bytes to verify + ** bufferSize = the size of the verify buffer + ** bytesVerified = number of bytes verified + ** byteCount = number of bytes to verify this pass + ** startVerify = position in buffPtr + */ + bytesVerified = 0; + startVerify = (Ptr)buffPtr; + while ( (bytesVerified < *count) && ( result == noErr ) ) + { + if ( (*count - bytesVerified) > bufferSize ) + { + byteCount = bufferSize; + } + else + { + byteCount = *count - bytesVerified; + } + /* + ** Copy the write buffer into the verify buffer. + ** This step is needed because the File Manager + ** compares the data in any non-block aligned + ** data at the beginning and end of the read-verify + ** request back into the file system's cache + ** to the data in verify Buffer. However, the + ** File Manager does not compare any full blocks + ** and instead copies them into the verify buffer + ** so we still have to compare the buffers again + ** after the read-verify request completes. + */ + BlockMoveData(startVerify, verifyBuffer, byteCount); + + /* Read-verify the data back into the verify buffer */ + result = FSReadVerify(refNum, &byteCount, verifyBuffer); + if ( result == noErr ) + { + /* See if the buffers are the same */ + if ( !EqualMemory(verifyBuffer, startVerify, byteCount) ) + { + result = ioErr; + } + startVerify += byteCount; + bytesVerified += byteCount; + } + } + } + } + } + DisposePtr(verifyBuffer); + } + else + { + result = memFullErr; + } + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr CopyFork(short srcRefNum, + short dstRefNum, + void *copyBufferPtr, + long copyBufferSize) +{ + ParamBlockRec srcPB; + ParamBlockRec dstPB; + OSErr srcError; + OSErr dstError; + + if ( (copyBufferPtr == NULL) || (copyBufferSize == 0) ) + return ( paramErr ); + + srcPB.ioParam.ioRefNum = srcRefNum; + dstPB.ioParam.ioRefNum = dstRefNum; + + /* preallocate the destination fork and */ + /* ensure the destination fork's EOF is correct after the copy */ + srcError = PBGetEOFSync(&srcPB); + if ( srcError != noErr ) + return ( srcError ); + dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc; + dstError = PBSetEOFSync(&dstPB); + if ( dstError != noErr ) + return ( dstError ); + + /* reset source fork's mark */ + srcPB.ioParam.ioPosMode = fsFromStart; + srcPB.ioParam.ioPosOffset = 0; + srcError = PBSetFPosSync(&srcPB); + if ( srcError != noErr ) + return ( srcError ); + + /* reset destination fork's mark */ + dstPB.ioParam.ioPosMode = fsFromStart; + dstPB.ioParam.ioPosOffset = 0; + dstError = PBSetFPosSync(&dstPB); + if ( dstError != noErr ) + return ( dstError ); + + /* set up fields that won't change in the loop */ + srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr; + srcPB.ioParam.ioPosMode = fsAtMark + noCacheMask;/* fsAtMark + noCacheMask */ + /* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */ + /* This will make writes on local volumes faster */ + if ( (copyBufferSize >= 512) && ((copyBufferSize & 0x1ff) != 0) ) + { + srcPB.ioParam.ioReqCount = copyBufferSize & 0xfffffe00; + } + else + { + srcPB.ioParam.ioReqCount = copyBufferSize; + } + dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr; + dstPB.ioParam.ioPosMode = fsAtMark + noCacheMask;/* fsAtMark + noCacheMask */ + + while ( (srcError == noErr) && (dstError == noErr) ) + { + srcError = PBReadSync(&srcPB); + dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount; + dstError = PBWriteSync(&dstPB); + } + + /* make sure there were no errors at the destination */ + if ( dstError != noErr ) + return ( dstError ); + + /* make sure the only error at the source was eofErr */ + if ( srcError != eofErr ) + return ( srcError ); + + return ( noErr ); +} + +/*****************************************************************************/ + +pascal OSErr GetFileLocation(short refNum, + short *vRefNum, + long *dirID, + StringPtr fileName) +{ + FCBPBRec pb; + OSErr error; + + pb.ioNamePtr = fileName; + pb.ioVRefNum = 0; + pb.ioRefNum = refNum; + pb.ioFCBIndx = 0; + error = PBGetFCBInfoSync(&pb); + if ( error == noErr ) + { + *vRefNum = pb.ioFCBVRefNum; + *dirID = pb.ioFCBParID; + } + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpGetFileLocation(short refNum, + FSSpec *spec) +{ + return ( GetFileLocation(refNum, &(spec->vRefNum), &(spec->parID), spec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr CopyDirectoryAccess(short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName) +{ + OSErr error; + GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */ + long dstServerAdr; /* AppleTalk server address of destination (if any) */ + long ownerID, groupID, accessRights; + long tempLong; + + /* See if destination supports directory access control */ + tempLong = sizeof(infoBuffer); + error = HGetVolParms(dstName, dstVRefNum, &infoBuffer, &tempLong); + if ( (error == noErr) && hasAccessCntl(&infoBuffer) ) + { + if ( hasAccessCntl(&infoBuffer) ) + { + dstServerAdr = infoBuffer.vMServerAdr; + + /* See if source supports directory access control and is on same server */ + tempLong = sizeof(infoBuffer); + error = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong); + if ( error == noErr ) + { + if ( hasAccessCntl(&infoBuffer) && (dstServerAdr == infoBuffer.vMServerAdr) ) + { + /* both volumes support directory access control and they are */ + /* on same server, so copy the access information */ + error = HGetDirAccess(srcVRefNum, srcDirID, srcName, &ownerID, &groupID, &accessRights); + if ( error == noErr ) + { + error = HSetDirAccess(dstVRefNum, dstDirID, dstName, ownerID, groupID, accessRights); + } + } + else + { + /* destination doesn't support directory access control or */ + /* they volumes aren't on the same server */ + error = paramErr; + } + } + } + else + { + /* destination doesn't support directory access control */ + error = paramErr; + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpCopyDirectoryAccess(const FSSpec *srcSpec, + const FSSpec *dstSpec) +{ + return ( CopyDirectoryAccess(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->vRefNum, dstSpec->parID, dstSpec->name) ); +} + +/*****************************************************************************/ + +pascal OSErr HMoveRenameCompat(short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName) +{ + OSErr error; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize; + short realVRefNum; + long realParID; + Str31 realName; + Boolean isDirectory; + long tempItemsDirID; + long uniqueTempDirID; + Str31 uniqueTempDirName; + unsigned short uniqueNameoverflow; + + /* Get volume attributes */ + infoSize = sizeof(GetVolParmsInfoBuffer); + error = HGetVolParms((StringPtr)srcName, vRefNum, &volParmsInfo, &infoSize); + if ( (error == noErr) && hasMoveRename(&volParmsInfo) ) + { + /* If volume supports move and rename, so use it and return */ + error = HMoveRename(vRefNum, srcDirID, srcName, dstDirID, dstpathName, copyName); + } + else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */ + { + /* MoveRename isn't supported by this volume, so do it by hand */ + + /* If copyName isn't supplied, we can simply CatMove and return */ + if ( copyName == NULL ) + { + error = CatMove(vRefNum, srcDirID, srcName, dstDirID, dstpathName); + } + else + { + /* Renaming is required, so we have some work to do... */ + + /* Get the object's real name, real parent ID and real vRefNum */ + error = GetObjectLocation(vRefNum, srcDirID, (StringPtr)srcName, + &realVRefNum, &realParID, realName, &isDirectory); + if ( error == noErr ) + { + /* Find the Temporary Items Folder on that volume */ + error = FindFolder(realVRefNum, kTemporaryFolderType, kCreateFolder, + &realVRefNum, &tempItemsDirID); + if ( error == noErr ) + { + /* Create a new uniquely named folder in the temporary items folder. */ + /* This is done to avoid the case where 'realName' or 'copyName' already */ + /* exists in the temporary items folder. */ + + /* Start with current tick count as uniqueTempDirName */ + NumToString(TickCount(), uniqueTempDirName); + uniqueNameoverflow = 0; + do + { + error = DirCreate(realVRefNum, tempItemsDirID, uniqueTempDirName, &uniqueTempDirID); + if ( error == dupFNErr ) + { + /* Duplicate name - change the first character to the next ASCII character */ + ++uniqueTempDirName[1]; + /* Make sure it isn't a colon! */ + if ( uniqueTempDirName[1] == ':' ) + { + ++uniqueTempDirName[1]; + } + /* Don't go too far... */ + ++uniqueNameoverflow; + } + } while ( (error == dupFNErr) && (uniqueNameoverflow <= 64) ); /* 64 new files per 1/60th second - not likely! */ + if ( error == noErr ) + { + /* Move the object to the folder with uniqueTempDirID for renaming */ + error = CatMove(realVRefNum, realParID, realName, uniqueTempDirID, NULL); + if ( error == noErr ) + { + /* Rename the object */ + error = HRename(realVRefNum, uniqueTempDirID, realName, copyName); + if ( error == noErr ) + { + /* Move object to its new home */ + error = CatMove(realVRefNum, uniqueTempDirID, copyName, dstDirID, dstpathName); + if ( error != noErr ) + { + /* Error handling: rename object back to original name - ignore errors */ + (void) HRename(realVRefNum, uniqueTempDirID, copyName, realName); + } + } + if ( error != noErr ) + { + /* Error handling: move object back to original location - ignore errors */ + (void) CatMove(realVRefNum, uniqueTempDirID, realName, realParID, NULL); + } + } + /* Done with ourTempDir, so delete it - ignore errors */ + (void) HDelete(realVRefNum, uniqueTempDirID, NULL); + } + } + } + } + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr FSpMoveRenameCompat(const FSSpec *srcSpec, + const FSSpec *dstSpec, + ConstStr255Param copyName) +{ + /* make sure the FSSpecs refer to the same volume */ + if (srcSpec->vRefNum != dstSpec->vRefNum) + return (diffVolErr); + return ( HMoveRenameCompat(srcSpec->vRefNum, srcSpec->parID, srcSpec->name, + dstSpec->parID, dstSpec->name, copyName) ); +} + +/*****************************************************************************/ + +pascal OSErr BuildAFPVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str32 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + AFPVolMountInfoPtr *afpInfoPtr) +{ + MyAFPVolMountInfoPtr infoPtr; + OSErr error; + + /* Allocate the AFPXVolMountInfo record */ + infoPtr = (MyAFPVolMountInfoPtr)NewPtrClear(sizeof(MyAFPVolMountInfo)); + if ( infoPtr != NULL ) + { + /* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */ + infoPtr->length = sizeof(MyAFPVolMountInfo); + infoPtr->media = AppleShareMediaType; + infoPtr->flags = flags; + infoPtr->nbpInterval = nbpInterval; + infoPtr->nbpCount = nbpCount; + infoPtr->uamType = uamType; + + infoPtr->zoneNameOffset = offsetof(MyAFPVolMountInfo, zoneName); + infoPtr->serverNameOffset = offsetof(MyAFPVolMountInfo, serverName); + infoPtr->volNameOffset = offsetof(MyAFPVolMountInfo, volName); + infoPtr->userNameOffset = offsetof(MyAFPVolMountInfo, userName); + infoPtr->userPasswordOffset = offsetof(MyAFPVolMountInfo, userPassword); + infoPtr->volPasswordOffset = offsetof(MyAFPVolMountInfo, volPassword); + + BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32)); + BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32)); + BlockMoveData(volName, infoPtr->volName, sizeof(Str27)); + BlockMoveData(userName, infoPtr->userName, sizeof(Str31)); + BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8)); + BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8)); + + *afpInfoPtr = (AFPVolMountInfoPtr)infoPtr; + error = noErr; + } + else + { + error = memFullErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPVolMountInfo(AFPVolMountInfoPtr afpInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName) +{ + StringPtr tempPtr; + OSErr error; + + /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */ + if ( afpInfoPtr->media == AppleShareMediaType ) + { + *flags = afpInfoPtr->flags; + *uamType = afpInfoPtr->uamType; + + if ( afpInfoPtr->zoneNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->zoneNameOffset); + BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->serverNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->serverNameOffset); + BlockMoveData(tempPtr, serverName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->volNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->volNameOffset); + BlockMoveData(tempPtr, volName, tempPtr[0] + 1); + } + + if ( afpInfoPtr->userNameOffset != 0) + { + tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->userNameOffset); + BlockMoveData(tempPtr, userName, tempPtr[0] + 1); + } + + error = noErr; + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr BuildAFPXVolMountInfo(short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str32 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + Str32 uamName, + unsigned long alternateAddressLength, + void *alternateAddress, + AFPXVolMountInfoPtr *afpXInfoPtr) +{ + Size infoSize; + MyAFPXVolMountInfoPtr infoPtr; + OSErr error; + + /* Calculate the size of the AFPXVolMountInfo record */ + infoSize = sizeof(MyAFPXVolMountInfo) + alternateAddressLength - 1; + + /* Allocate the AFPXVolMountInfo record */ + infoPtr = (MyAFPXVolMountInfoPtr)NewPtrClear(infoSize); + if ( infoPtr != NULL ) + { + /* Fill in an AFPXVolMountInfo record that can be passed to VolumeMount */ + infoPtr->length = infoSize; + infoPtr->media = AppleShareMediaType; + infoPtr->flags = flags; + if ( alternateAddressLength != 0 ) + { + /* make sure the volMountExtendedFlagsBit is set if there's extended address info */ + infoPtr->flags |= volMountExtendedFlagsMask; + /* and set the only extendedFlags bit we know about */ + infoPtr->extendedFlags = kAFPExtendedFlagsAlternateAddressMask; + } + else + { + /* make sure the volMountExtendedFlagsBit is clear if there's no extended address info */ + infoPtr->flags &= ~volMountExtendedFlagsMask; + /* and clear the extendedFlags */ + infoPtr->extendedFlags = 0; + } + infoPtr->nbpInterval = nbpInterval; + infoPtr->nbpCount = nbpCount; + infoPtr->uamType = uamType; + + infoPtr->zoneNameOffset = offsetof(MyAFPXVolMountInfo, zoneName); + infoPtr->serverNameOffset = offsetof(MyAFPXVolMountInfo, serverName); + infoPtr->volNameOffset = offsetof(MyAFPXVolMountInfo, volName); + infoPtr->userNameOffset = offsetof(MyAFPXVolMountInfo, userName); + infoPtr->userPasswordOffset = offsetof(MyAFPXVolMountInfo, userPassword); + infoPtr->volPasswordOffset = offsetof(MyAFPXVolMountInfo, volPassword); + infoPtr->uamNameOffset = offsetof(MyAFPXVolMountInfo, uamName); + infoPtr->alternateAddressOffset = offsetof(MyAFPXVolMountInfo, alternateAddress); + + BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32)); + BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32)); + BlockMoveData(volName, infoPtr->volName, sizeof(Str27)); + BlockMoveData(userName, infoPtr->userName, sizeof(Str31)); + BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8)); + BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8)); + BlockMoveData(uamName, infoPtr->uamName, sizeof(Str32)); + BlockMoveData(alternateAddress, infoPtr->alternateAddress, alternateAddressLength); + + *afpXInfoPtr = (AFPXVolMountInfoPtr)infoPtr; + error = noErr; + } + else + { + error = memFullErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr RetrieveAFPXVolMountInfo(AFPXVolMountInfoPtr afpXInfoPtr, + short *flags, + short *uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName, + StringPtr uamName, + unsigned long *alternateAddressLength, + AFPAlternateAddress **alternateAddress) +{ + StringPtr tempPtr; + Ptr alternateAddressStart; + Ptr alternateAddressEnd; + Size alternateAddressDataSize; + OSErr error; + UInt8 addressCount; + + /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */ + if ( afpXInfoPtr->media == AppleShareMediaType ) + { + /* default to noErr */ + error = noErr; + + /* Is this an extended record? */ + if ( (afpXInfoPtr->flags & volMountExtendedFlagsMask) != 0 ) + { + if ( ((afpXInfoPtr->extendedFlags & kAFPExtendedFlagsAlternateAddressMask) != 0) && + (afpXInfoPtr->alternateAddressOffset != 0) ) + { + + alternateAddressStart = (Ptr)((long)afpXInfoPtr + afpXInfoPtr->alternateAddressOffset); + alternateAddressEnd = alternateAddressStart + 1; /* skip over alternate address version byte */ + addressCount = *(UInt8*)alternateAddressEnd; /* get the address count */ + ++alternateAddressEnd; /* skip over alternate address count byte */ + /* alternateAddressEnd now equals &AFPAlternateAddress.fAddressList[0] */ + while ( addressCount != 0 ) + { + /* parse the address list to find the end */ + alternateAddressEnd += *(UInt8*)alternateAddressEnd; /* add length of each AFPTagData record */ + --addressCount; + } + /* get the size of the alternateAddressData */ + alternateAddressDataSize = alternateAddressEnd - alternateAddressStart; + /* allocate memory for it */ + *alternateAddress = (AFPAlternateAddress *)NewPtr(alternateAddressDataSize); + if ( *alternateAddress != NULL ) + { + /* and return the data */ + BlockMoveData(alternateAddressStart, *alternateAddress, alternateAddressDataSize); + *alternateAddressLength = alternateAddressDataSize; + } + else + { + /* no memory - fail now */ + error = memFullErr; + } + } + + if ( error == noErr ) /* fill in more output parameters if everything is OK */ + { + if ( afpXInfoPtr->uamNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->uamNameOffset); + BlockMoveData(tempPtr, uamName, tempPtr[0] + 1); + } + } + } + + if ( error == noErr ) /* fill in more output parameters if everything is OK */ + { + *flags = afpXInfoPtr->flags; + *uamType = afpXInfoPtr->uamType; + + if ( afpXInfoPtr->zoneNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->zoneNameOffset); + BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->serverNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->serverNameOffset); + BlockMoveData(tempPtr, serverName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->volNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->volNameOffset); + BlockMoveData(tempPtr, volName, tempPtr[0] + 1); + } + + if ( afpXInfoPtr->userNameOffset != 0 ) + { + tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->userNameOffset); + BlockMoveData(tempPtr, userName, tempPtr[0] + 1); + } + } + } + else + { + error = paramErr; + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr GetUGEntries(short objType, + UGEntryPtr entries, + long reqEntryCount, + long *actEntryCount, + long *objID) +{ + HParamBlockRec pb; + OSErr error = noErr; + UGEntry *endEntryArray; + + pb.objParam.ioObjType = objType; + *actEntryCount = 0; + for ( endEntryArray = entries + reqEntryCount; (entries < endEntryArray) && (error == noErr); ++entries ) + { + pb.objParam.ioObjNamePtr = (StringPtr)entries->name; + pb.objParam.ioObjID = *objID; + /* Files.h in the universal interfaces, PBGetUGEntrySync takes a CMovePBPtr */ + /* as the parameter. Inside Macintosh and the original glue used HParmBlkPtr. */ + /* A CMovePBPtr works OK, but this will be changed in the future back to */ + /* HParmBlkPtr, so I'm just casting it here. */ + error = PBGetUGEntrySync(&pb); + if ( error == noErr ) + { + entries->objID = *objID = pb.objParam.ioObjID; + entries->objType = objType; + ++*actEntryCount; + } + } + + return ( error ); +} + +/*****************************************************************************/ + diff --git a/src/osx/carbon/morefile/MoreFilesExtras.h b/src/osx/carbon/morefile/MoreFilesExtras.h new file mode 100644 index 0000000000..285dfa7977 --- /dev/null +++ b/src/osx/carbon/morefile/MoreFilesExtras.h @@ -0,0 +1,3597 @@ +/* + File: MoreFilesExtras.h + + Contains: A collection of useful high-level File Manager routines. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __MOREFILESEXTRAS__ +#define __MOREFILESEXTRAS__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +/* +** Bit masks and macros to get common information out of ioACUser returned +** by PBGetCatInfo (remember to clear ioACUser before calling PBGetCatInfo +** since some file systems don't bother to set this field). +** +** Use the GetDirAccessRestrictions or FSpGetDirAccessRestrictions +** functions to retrieve the ioACUser access restrictions byte for +** a folder. +** +** Note: The access restriction byte returned by PBGetCatInfo is the +** 2's complement of the user's privileges byte returned in +** ioACAccess by PBHGetDirAccess. +*/ + +enum { + /* mask for just the access restriction bits */ + acUserAccessMask = (kioACUserNoSeeFolderMask + kioACUserNoSeeFilesMask + kioACUserNoMakeChangesMask), /* common access privilege settings */ + acUserFull = 0x00, /* no access restiction bits on */ + acUserNone = acUserAccessMask, /* all access restiction bits on */ + acUserDropBox = kioACUserNoSeeFolderMask + kioACUserNoSeeFilesMask, /* make changes, but not see files or folders */ + acUserBulletinBoard = kioACUserNoMakeChangesMask /* see files and folders, but not make changes */ +}; + + +/*****************************************************************************/ + +/* +** Deny mode permissions for use with the HOpenAware, HOpenRFAware, +** FSpOpenAware, and FSpOpenRFAware functions. +** Note: Common settings are the ones with comments. +*/ + +enum { + dmNone = 0x0000, + dmNoneDenyRd = fsRdDenyPerm, + dmNoneDenyWr = fsWrDenyPerm, + dmNoneDenyRdWr = (fsRdDenyPerm + fsWrDenyPerm), + dmRd = fsRdPerm, /* Single writer, multiple readers; the readers */ + dmRdDenyRd = (fsRdPerm + fsRdDenyPerm), + dmRdDenyWr = (fsRdPerm + fsWrDenyPerm), /* Browsing - equivalent to fsRdPerm */ + dmRdDenyRdWr = (fsRdPerm + fsRdDenyPerm + fsWrDenyPerm), + dmWr = fsWrPerm, + dmWrDenyRd = (fsWrPerm + fsRdDenyPerm), + dmWrDenyWr = (fsWrPerm + fsWrDenyPerm), + dmWrDenyRdWr = (fsWrPerm + fsRdDenyPerm + fsWrDenyPerm), + dmRdWr = fsRdWrPerm, /* Shared access - equivalent to fsRdWrShPerm */ + dmRdWrDenyRd = (fsRdWrPerm + fsRdDenyPerm), + dmRdWrDenyWr = (fsRdWrPerm + fsWrDenyPerm), /* Single writer, multiple readers; the writer */ + dmRdWrDenyRdWr = (fsRdWrPerm + fsRdDenyPerm + fsWrDenyPerm) /* Exclusive access - equivalent to fsRdWrPerm */ +}; + + +/*****************************************************************************/ + +/* +** For those times where you need to use more than one kind of File Manager parameter +** block but don't feel like wasting stack space, here's a parameter block you can reuse. +*/ + + +union UniversalFMPB { + ParamBlockRec PB; + CInfoPBRec ciPB; + DTPBRec dtPB; + HParamBlockRec hPB; + CMovePBRec cmPB; + WDPBRec wdPB; + FCBPBRec fcbPB; + XVolumeParam xPB; +}; +typedef union UniversalFMPB UniversalFMPB; +typedef UniversalFMPB * UniversalFMPBPtr; +typedef UniversalFMPBPtr * UniversalFMPBHandle; + +/* +** Used by GetUGEntries to return user or group lists +*/ + +struct UGEntry { + short objType; /* object type: -1 = group; 0 = user */ + long objID; /* the user or group ID */ + Str31 name; /* the user or group name */ +}; +typedef struct UGEntry UGEntry; +typedef UGEntry * UGEntryPtr; +typedef UGEntryPtr * UGEntryHandle; + +/* +** I use the following records instead of the AFPVolMountInfo and AFPXVolMountInfo structures in Files.h +*/ +typedef unsigned char Str8[9]; + +struct MyAFPVolMountInfo { + short length; /* length of this record */ + VolumeType media; /* type of media, always AppleShareMediaType */ + short flags; /* 0 = normal mount; set bit 0 to inhibit greeting messages */ + char nbpInterval; /* NBP interval parameter; 7 is a good choice */ + char nbpCount; /* NBP count parameter; 5 is a good choice */ + short uamType; /* User Authentication Method */ + short zoneNameOffset; /* offset from start of record to zoneName */ + short serverNameOffset; /* offset from start of record to serverName */ + short volNameOffset; /* offset from start of record to volName */ + short userNameOffset; /* offset from start of record to userName */ + short userPasswordOffset; /* offset from start of record to userPassword */ + short volPasswordOffset; /* offset from start of record to volPassword */ + Str32 zoneName; /* server's AppleTalk zone name */ + char filler1; /* to word align volPassword */ + Str32 serverName; /* server name */ + char filler2; /* to word align volPassword */ + Str27 volName; /* volume name */ + Str31 userName; /* user name (zero length Pascal string for guest) */ + Str8 userPassword; /* user password (zero length Pascal string if no user password) */ + char filler3; /* to word align volPassword */ + Str8 volPassword; /* volume password (zero length Pascal string if no volume password) */ + char filler4; /* to end record on word boundry */ +}; +typedef struct MyAFPVolMountInfo MyAFPVolMountInfo; +typedef MyAFPVolMountInfo * MyAFPVolMountInfoPtr; +typedef MyAFPVolMountInfoPtr * MyAFPVolMountInfoHandle; + +struct MyAFPXVolMountInfo { + short length; /* length of this record */ + VolumeType media; /* type of media, always AppleShareMediaType */ + short flags; /* bits for no messages, no reconnect, etc */ + char nbpInterval; /* NBP interval parameter; 7 is a good choice */ + char nbpCount; /* NBP count parameter; 5 is a good choice */ + short uamType; /* User Authentication Method */ + short zoneNameOffset; /* offset from start of record to zoneName */ + short serverNameOffset; /* offset from start of record to serverName */ + short volNameOffset; /* offset from start of record to volName */ + short userNameOffset; /* offset from start of record to userName */ + short userPasswordOffset; /* offset from start of record to userPassword */ + short volPasswordOffset; /* offset from start of record to volPassword */ + short extendedFlags; /* extended flags word */ + short uamNameOffset; /* offset to a pascal UAM name string */ + short alternateAddressOffset; /* offset to Alternate Addresses in tagged format */ + Str32 zoneName; /* server's AppleTalk zone name */ + char filler1; /* to word align volPassword */ + Str32 serverName; /* server name */ + char filler2; /* to word align volPassword */ + Str27 volName; /* volume name */ + Str31 userName; /* user name (zero length Pascal string for guest) */ + Str8 userPassword; /* user password (zero length Pascal string if no user password) */ + char filler3; /* to word align volPassword */ + Str8 volPassword; /* volume password (zero length Pascal string if no volume password) */ + char filler4; /* to word align uamNameOffset */ + Str32 uamName; /* UAM name */ + char filler5; /* to word align alternateAddress */ + char alternateAddress[1]; /* AFPAlternateAddress */ +}; +typedef struct MyAFPXVolMountInfo MyAFPXVolMountInfo; +typedef MyAFPXVolMountInfo * MyAFPXVolMountInfoPtr; +typedef MyAFPXVolMountInfoPtr * MyAFPXVolMountInfoHandle; + +/*****************************************************************************/ + +/* Functions to get information out of GetVolParmsInfoBuffer. */ + +/* version 1 field getters */ + +EXTERN_API( short ) +GetVolParmsInfoVersion(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( long ) +GetVolParmsInfoAttrib(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Handle ) +GetVolParmsInfoLocalHand(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( long ) +GetVolParmsInfoServerAdr(const GetVolParmsInfoBuffer * volParms); + + + +/* version 2 field getters (assume zero result if version < 2) */ + +EXTERN_API( long ) +GetVolParmsInfoVolumeGrade(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( long ) +GetVolParmsInfoForeignPrivID(const GetVolParmsInfoBuffer * volParms); + + + +/* version 3 field getters (assume zero result if version < 3) */ + +EXTERN_API( long ) +GetVolParmsInfoExtendedAttributes(const GetVolParmsInfoBuffer * volParms); + + + +/* attribute bits supported by all versions of GetVolParmsInfoBuffer */ + +EXTERN_API( Boolean ) +isNetworkVolume(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasLimitFCBs(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasLocalWList(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasNoMiniFndr(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasNoVNEdit(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasNoLclSync(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasTrshOffLine(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasNoSwitchTo(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasNoDeskItems(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasNoBootBlks(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasAccessCntl(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasNoSysDir(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasExtFSVol(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasOpenDeny(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasCopyFile(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasMoveRename(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasDesktopMgr(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasShortName(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasFolderLock(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasPersonalAccessPrivileges(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasUserGroupList(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasCatSearch(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasFileIDs(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasBTreeMgr(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +hasBlankAccessPrivileges(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +supportsAsyncRequests(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +supportsTrashVolumeCache(const GetVolParmsInfoBuffer * volParms); + + + +/* attribute bits supported by version 3 and greater versions of GetVolParmsInfoBuffer */ + +EXTERN_API( Boolean ) +volIsEjectable(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupportsHFSPlusAPIs(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupportsFSCatalogSearch(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupportsFSExchangeObjects(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupports2TBFiles(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupportsLongNames(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupportsMultiScriptNames(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupportsNamedForks(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volSupportsSubtreeIterators(const GetVolParmsInfoBuffer * volParms); + + +EXTERN_API( Boolean ) +volL2PCanMapFileBlocks(const GetVolParmsInfoBuffer * volParms); + + + +/*****************************************************************************/ + +/* Functions for testing ioACUser bits. */ + +EXTERN_API( Boolean ) +userIsOwner(SInt8 ioACUser); + + +EXTERN_API( Boolean ) +userHasFullAccess(SInt8 ioACUser); + + +EXTERN_API( Boolean ) +userHasDropBoxAccess(SInt8 ioACUser); + + +EXTERN_API( Boolean ) +userHasBulletinBoard(SInt8 ioACUser); + + +EXTERN_API( Boolean ) +userHasNoAccess(SInt8 ioACUser); + + + +/*****************************************************************************/ + +EXTERN_API( void ) +TruncPString( + StringPtr destination, + ConstStr255Param source, + short maxLength); + + +/* + The TruncPString function copies up to maxLength characters from + the source Pascal string to the destination Pascal string. TruncPString + ensures that the truncated string ends on a single-byte character, or on + the last byte of a multi-byte character. + + destination output: destination Pascal string. + source input: source Pascal string. + maxLength output: The maximum allowable length of the destination + string. +*/ + +/*****************************************************************************/ + +EXTERN_API( Ptr ) +GetTempBuffer( + long buffReqSize, + long * buffActSize); + + +/* + The GetTempBuffer function allocates a temporary buffer for file system + operations which is at least 1024 bytes (1K) and a multiple of + 1024 bytes. + + buffReqSize input: Size you'd like the buffer to be. + buffActSize output: Size of buffer allocated. + function result output: Pointer to memory allocated or nil if no memory + was available. The caller is responsible for + disposing of this buffer with DisposePtr. +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetVolumeInfoNoName( + ConstStr255Param pathname, + short vRefNum, + HParmBlkPtr pb); + + +/* + GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync + in cases where the returned volume name is not needed by the caller. + The pathname and vRefNum parameters are not touched, and the pb + parameter is initialized by PBHGetVInfoSync except that ioNamePtr in + the parameter block is always returned as NULL (since it might point + to GetVolumeInfoNoName's local variable tempPathname). + + I noticed using this code in several places, so here it is once. + This reduces the code size of MoreFiles. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + pb input: A pointer to HParamBlockRec. + output: The parameter block as filled in by PBHGetVInfoSync + except that ioNamePtr will always be NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +XGetVolumeInfoNoName( + ConstStr255Param pathname, + short vRefNum, + XVolumeParamPtr pb); + + +/* + XGetVolumeInfoNoName uses pathname and vRefNum to call PBXGetVolInfoSync + in cases where the returned volume name is not needed by the caller. + The pathname and vRefNum parameters are not touched, and the pb + parameter is initialized by PBXGetVolInfoSync except that ioNamePtr in + the parameter block is always returned as NULL (since it might point + to XGetVolumeInfoNoName's local variable tempPathname). + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + pb input: A pointer to HParamBlockRec. + output: The parameter block as filled in by PBXGetVolInfoSync + except that ioNamePtr will always be NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetCatInfoNoName( + short vRefNum, + long dirID, + ConstStr255Param name, + CInfoPBPtr pb); + + +/* + GetCatInfoNoName uses vRefNum, dirID and name to call PBGetCatInfoSync + in cases where the returned object is not needed by the caller. + The vRefNum, dirID and name parameters are not touched, and the pb + parameter is initialized by PBGetCatInfoSync except that ioNamePtr in + the parameter block is always returned as NULL (since it might point + to GetCatInfoNoName's local variable tempName). + + I noticed using this code in several places, so here it is once. + This reduces the code size of MoreFiles. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + pb input: A pointer to CInfoPBRec. + output: The parameter block as filled in by + PBGetCatInfoSync except that ioNamePtr will + always be NULL. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DetermineVRefNum( + ConstStr255Param pathname, + short vRefNum, + short * realVRefNum); + + +/* + The DetermineVRefNum function determines the volume reference number of + a volume from a pathname, a volume specification, or a combination + of the two. + WARNING: Volume names on the Macintosh are *not* unique -- Multiple + mounted volumes can have the same name. For this reason, the use of a + volume name or full pathname to identify a specific volume may not + produce the results you expect. If more than one volume has the same + name and a volume name or full pathname is used, the File Manager + currently uses the first volume it finds with a matching name in the + volume queue. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + realVRefNum output: The real volume reference number. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HGetVInfo( + short volReference, + StringPtr volName, + short * vRefNum, + unsigned long * freeBytes, + unsigned long * totalBytes); + + +/* + The HGetVInfo function returns the name, volume reference number, + available space (in bytes), and total space (in bytes) for the + specified volume. You can specify the volume by providing its drive + number, volume reference number, or 0 for the default volume. + This routine is compatible with volumes up to 4 gigabytes. + + volReference input: The drive number, volume reference number, + or 0 for the default volume. + volName input: A pointer to a buffer (minimum Str27) where + the volume name is to be returned or must + be nil. + output: The volume name. + vRefNum output: The volume reference number. + freeBytes output: The number of free bytes on the volume. + freeBytes is an unsigned long value. + totalBytes output: The total number of bytes on the volume. + totalBytes is an unsigned long value. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume + + __________ + + Also see: XGetVInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +XGetVInfo( + short volReference, + StringPtr volName, + short * vRefNum, + UInt64 * freeBytes, + UInt64 * totalBytes); + + +/* + The XGetVInfo function returns the name, volume reference number, + available space (in bytes), and total space (in bytes) for the + specified volume. You can specify the volume by providing its drive + number, volume reference number, or 0 for the default volume. + This routine is compatible with volumes up to 2 terabytes. + + volReference input: The drive number, volume reference number, + or 0 for the default volume. + volName input: A pointer to a buffer (minimum Str27) where + the volume name is to be returned or must + be nil. + output: The volume name. + vRefNum output: The volume reference number. + freeBytes output: The number of free bytes on the volume. + freeBytes is an UnsignedWide value. + totalBytes output: The total number of bytes on the volume. + totalBytes is an UnsignedWide value. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume + + __________ + + Also see: HGetVInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +CheckVolLock( + ConstStr255Param pathname, + short vRefNum); + + +/* + The CheckVolLock function determines if a volume is locked - either by + hardware or by software. If CheckVolLock returns noErr, then the volume + is not locked. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + + Result Codes + noErr 0 No error - volume not locked + nsvErr -35 No such volume + wPrErr -44 Volume locked by hardware + vLckdErr -46 Volume locked by software + paramErr -50 No default volume +*/ + +/*****************************************************************************/ +/* +** The following routines call Mac OS routines that are not supported by +** Carbon: +** +** GetDriverName +** FindDrive +** GetDiskBlocks +** GetVolState +*/ + +#if !TARGET_API_MAC_CARBON // { + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetDriverName( + short driverRefNum, + Str255 driverName); + + +/* + The GetDriverName function returns a device driver's name. + + driverRefNum input: The driver reference number. + driverName output: The driver's name. + + Result Codes + noErr 0 No error + badUnitErr -21 Bad driver reference number +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FindDrive( + ConstStr255Param pathname, + short vRefNum, + DrvQElPtr * driveQElementPtr); + + +/* + The FindDrive function returns a pointer to a mounted volume's + drive queue element. + + pathName input: Pointer to a full pathname or nil. If you + pass in a partial pathname, it is ignored. + A full pathname to a volume must end with + a colon character (:). + vRefNum input: Volume specification (volume reference + number, working directory number, drive + number, or 0). + driveQElementPtr output: Pointer to a volume's drive queue element + in the drive queue. DO NOT change the + DrvQEl. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume + nsDrvErr -56 No such drive +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetDiskBlocks( + ConstStr255Param pathname, + short vRefNum, + unsigned long * numBlocks); + + +/* + The GetDiskBlocks function returns the number of physical disk + blocks on a disk drive. NOTE: This is not the same as volume + allocation blocks! + + pathName input: Pointer to a full pathname or nil. If you + pass in a partial pathname, it is ignored. + A full pathname to a volume must end with + a colon character (:). + vRefNum input: Volume specification (volume reference + number, working directory number, drive + number, or 0). + numBlocks output: The number of physical disk blocks on the disk drive. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, driver reference + number is zero, ReturnFormatList + returned zero blocks, DriveStatus + returned an unknown value, or + driveQElementPtr->qType is unknown + nsDrvErr -56 No such drive + statusErr Ð18 Driver does not respond to this + status request + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies + a nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetVolState( + ConstStr255Param pathname, + short vRefNum, + Boolean * volumeOnline, + Boolean * volumeEjected, + Boolean * driveEjectable, + Boolean * driverWantsEject); + + +/* + The GetVolState function determines if a volume is online or offline, + if an offline volume is ejected, and if the volume's driver is + ejectable or wants eject calls. + + pathName input: Pointer to a full pathname or nil. + vRefNum input: Volume specification (volume reference number, + working directory number, drive number, or 0). + volumeOnline output: True if the volume is online; + False if the volume is offline. + volumeEjected output: True if the volume is ejected (ejected + volumes are always offline); False if the + volume is not ejected. + driveEjectable output: True if the volume's drive is ejectable; + False if the volume's drive is not ejectable. + driverWantsEject output: True if the volume's driver wants an Eject + request after unmount (even if the drive + is not ejectable); False if the volume's + driver does not need an eject request. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +#endif // } !TARGET_API_MAC_CARBON + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetVolFileSystemID( + ConstStr255Param pathname, + short vRefNum, + short * fileSystemID); + + +/* + The GetVolFileSystemID function returned the file system ID of + a mounted volume. The file system ID identifies the file system + that handles requests to a particular volume. Here's a partial list + of file system ID numbers (only Apple's file systems are listed): + FSID File System + ----- ----------------------------------------------------- + $0000 Macintosh HFS or MFS + $0100 ProDOS File System + $0101 PowerTalk Mail Enclosures + $4147 ISO 9660 File Access (through Foreign File Access) + $4242 High Sierra File Access (through Foreign File Access) + $464D QuickTake File System (through Foreign File Access) + $4953 Macintosh PC Exchange (MS-DOS) + $4A48 Audio CD Access (through Foreign File Access) + $4D4B Apple Photo Access (through Foreign File Access) + + See the Technical Note "FL 35 - Determining Which File System + Is Active" and the "Guide to the File System Manager" for more + information. + + pathName input: Pointer to a full pathname or nil. If you pass + in a partial pathname, it is ignored. A full + pathname to a volume must contain at least + one colon character (:) and must not start with + a colon character. + vRefNum input: Volume specification (volume reference number, + working directory number, drive number, or 0). + fileSystemID output: The volume's file system ID. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + paramErr -50 No default volume, or pb was NULL +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +UnmountAndEject( + ConstStr255Param pathname, + short vRefNum); + + +/* + The UnmountAndEject function unmounts and ejects a volume. The volume + is ejected only if it is ejectable and not already ejected. + + pathName input: Pointer to a full pathname or nil. If you pass in a + partial pathname, it is ignored. A full pathname to a + volume must end with a colon character (:). + vRefNum input: Volume specification (volume reference number, working + directory number, drive number, or 0). + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad volume name + fBsyErr -47 One or more files are open + paramErr -50 No default volume + nsDrvErr -56 No such drive + extFSErr -58 External file system error - no file + system claimed this call. +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +OnLine( + FSSpecPtr volumes, + short reqVolCount, + short * actVolCount, + short * volIndex); + + +/* + The OnLine function returns the list of volumes currently mounted in + an array of FSSpec records. + + A noErr result indicates that the volumes array was filled + (actVolCount == reqVolCount) and there may be additional volumes + mounted. A nsvErr result indicates that the end of the volume list + was found and actVolCount volumes were actually found this time. + + volumes input: Pointer to array of FSSpec where the volume list + is returned. + reqVolCount input: Maximum number of volumes to return (the number of + elements in the volumes array). + actVolCount output: The number of volumes actually returned. + volIndex input: The current volume index position. Set to 1 to + start with the first volume. + output: The volume index position to get the next volume. + Pass this value the next time you call OnLine to + start where you left off. + + Result Codes + noErr 0 No error, but there are more volumes + to list + nsvErr -35 No more volumes to be listed + paramErr -50 volIndex was <= 0 +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +SetDefault( + short newVRefNum, + long newDirID, + short * oldVRefNum, + long * oldDirID); + + +/* + The SetDefault function sets the default volume and directory to the + volume specified by newVRefNum and the directory specified by newDirID. + The current default volume reference number and directory ID are + returned in oldVRefNum and oldDir and must be used to restore the + default volume and directory to their previous state *as soon as + possible* with the RestoreDefault function. These two functions are + designed to be used as a wrapper around Standard I/O routines where + the location of the file is implied to be the default volume and + directory. In other words, this is how you should use these functions: + + error = SetDefault(newVRefNum, newDirID, &oldVRefNum, &oldDirID); + if ( error == noErr ) + { + // call the Stdio functions like remove, rename, tmpfile, + // fopen, freopen, etc. or non-ANSI extensions like + // fdopen,fsetfileinfo, -- create, open, unlink, etc. here! + + error = RestoreDefault(oldVRefNum, oldDirID); + } + + By using these functions as a wrapper, you won't need to open a working + directory (because SetDefault and RestoreDefault use HSetVol) and you + won't have to worry about the effects of using HSetVol (documented in + Technical Note "FL 11 - PBHSetVol is Dangerous" and in the + Inside Macintosh: Files book in the description of the HSetVol and + PBHSetVol functions) because the default volume/directory is restored + before giving up control to code that might be affected by HSetVol. + + newVRefNum input: Volume specification (volume reference number, + working directory number, drive number, or 0) of + the new default volume. + newDirID input: Directory ID of the new default directory. + oldVRefNum output: The volume specification to save for use with + RestoreDefault. + oldDirID output: The directory ID to save for use with + RestoreDefault. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + bdNamErr -37 Bad volume name + fnfErr -43 Directory not found + paramErr -50 No default volume + afpAccessDenied -5000 User does not have access to the directory + + __________ + + Also see: RestoreDefault +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +RestoreDefault( + short oldVRefNum, + long oldDirID); + + +/* + The RestoreDefault function restores the default volume and directory + to the volume specified by oldVRefNum and the directory specified by + oldDirID. The oldVRefNum and oldDirID parameters were previously + obtained from the SetDefault function. These two functions are designed + to be used as a wrapper around Standard C I/O routines where the + location of the file is implied to be the default volume and directory. + In other words, this is how you should use these functions: + + error = SetDefault(newVRefNum, newDirID, &oldVRefNum, &oldDirID); + if ( error == noErr ) + { + // call the Stdio functions like remove, rename, tmpfile, + // fopen, freopen, etc. or non-ANSI extensions like + // fdopen,fsetfileinfo, -- create, open, unlink, etc. here! + + error = RestoreDefault(oldVRefNum, oldDirID); + } + + By using these functions as a wrapper, you won't need to open a working + directory (because SetDefault and RestoreDefault use HSetVol) and you + won't have to worry about the effects of using HSetVol (documented in + Technical Note "FL 11 - PBHSetVol is Dangerous" and in the + Inside Macintosh: Files book in the description of the HSetVol and + PBHSetVol functions) because the default volume/directory is restored + before giving up control to code that might be affected by HSetVol. + + oldVRefNum input: The volume specification to restore. + oldDirID input: The directory ID to restore. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + bdNamErr -37 Bad volume name + fnfErr -43 Directory not found + paramErr -50 No default volume + rfNumErr -51 Bad working directory reference number + afpAccessDenied -5000 User does not have access to the directory + + __________ + + Also see: SetDefault +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetDInfo( + short vRefNum, + long dirID, + ConstStr255Param name, + DInfo * fndrInfo); + + +/* + The GetDInfo function gets the finder information for a directory. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + fndrInfo output: If the object is a directory, then its DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpGetDInfo, FSpGetFInfoCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetDInfo( + const FSSpec * spec, + DInfo * fndrInfo); + + +/* + The FSpGetDInfo function gets the finder information for a directory. + + spec input: An FSSpec record specifying the directory. + fndrInfo output: If the object is a directory, then its DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpGetFInfoCompat, GetDInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +SetDInfo( + short vRefNum, + long dirID, + ConstStr255Param name, + const DInfo * fndrInfo); + + +/* + The SetDInfo function sets the finder information for a directory. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + fndrInfo input: The DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpSetDInfo, FSpSetFInfoCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetDInfo( + const FSSpec * spec, + const DInfo * fndrInfo); + + +/* + The FSpSetDInfo function sets the finder information for a directory. + + spec input: An FSSpec record specifying the directory. + fndrInfo input: The DInfo. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpSetFInfoCompat, SetDInfo +*/ + +/*****************************************************************************/ + +#if OLDROUTINENAMES + #define GetDirID(vRefNum, dirID, name, theDirID, isDirectory) GetDirectoryID(vRefNum, dirID, name, theDirID, isDirectory) +#endif +EXTERN_API( OSErr ) +GetDirectoryID( + short vRefNum, + long dirID, + ConstStr255Param name, + long * theDirID, + Boolean * isDirectory); + + +/* + The GetDirectoryID function gets the directory ID number of the + directory specified. If a file is specified, then the parent + directory of the file is returned and isDirectory is false. If + a directory is specified, then that directory's ID number is + returned and isDirectory is true. + WARNING: Volume names on the Macintosh are *not* unique -- Multiple + mounted volumes can have the same name. For this reason, the use of a + volume name or full pathname to identify a specific volume may not + produce the results you expect. If more than one volume has the same + name and a volume name or full pathname is used, the File Manager + currently uses the first volume it finds with a matching name in the + volume queue. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + theDirID output: If the object is a file, then its parent directory + ID. If the object is a directory, then its ID. + isDirectory output: True if object is a directory; false if + object is a file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +#if OLDROUTINENAMES + #define DirIDFromFSSpec(spec, theDirID, isDirectory) FSpGetDirectoryID(spec, theDirID, isDirectory) +#endif +EXTERN_API( OSErr ) +FSpGetDirectoryID( + const FSSpec * spec, + long * theDirID, + Boolean * isDirectory); + + +/* + The FSpGetDirectoryID function gets the directory ID number of the + directory specified by spec. If spec is to a file, then the parent + directory of the file is returned and isDirectory is false. If + spec is to a directory, then that directory's ID number is + returned and isDirectory is true. + + spec input: An FSSpec record specifying the directory. + theDirID output: The directory ID. + isDirectory output: True if object is a directory; false if + object is a file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetDirName( + short vRefNum, + long dirID, + Str31 name); + + +/* + The GetDirName function gets the name of a directory from its + directory ID. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name output: Points to a Str31 where the directory name is to be + returned. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume or + name parameter was NULL + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetIOACUser( + short vRefNum, + long dirID, + ConstStr255Param name, + SInt8 * ioACUser); + + +/* + GetIOACUser returns a directory's access restrictions byte. + Use the masks and macro defined in MoreFilesExtras to check for + specific access priviledges. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + ioACUser output: The access restriction byte + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetIOACUser( + const FSSpec * spec, + SInt8 * ioACUser); + + +/* + FSpGetIOACUser returns a directory's access restrictions byte. + Use the masks and macro defined in MoreFilesExtras to check for + specific access priviledges. + + spec input: An FSSpec record specifying the directory. + ioACUser output: The access restriction byte + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetParentID( + short vRefNum, + long dirID, + ConstStr255Param name, + long * parID); + + +/* + The GetParentID function gets the parent directory ID number of the + specified object. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + parID output: The parent directory ID of the specified object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetFilenameFromPathname( + ConstStr255Param pathname, + Str255 filename); + + +/* + The GetFilenameFromPathname function gets the file (or directory) name + from the end of a full or partial pathname. Returns notAFileErr if the + pathname is nil, the pathname is empty, or the pathname cannot refer to + a filename (with a noErr result, the pathname could still refer to a + directory). + + pathname input: A full or partial pathname. + filename output: The file (or directory) name. + + Result Codes + noErr 0 No error + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + + __________ + + See also: GetObjectLocation. +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetObjectLocation( + short vRefNum, + long dirID, + ConstStr255Param pathname, + short * realVRefNum, + long * realParID, + Str255 realName, + Boolean * isDirectory); + + +/* + The GetObjectLocation function gets a file system object's location - + that is, its real volume reference number, real parent directory ID, + and name. While we're at it, determine if the object is a file or directory. + If GetObjectLocation returns fnfErr, then the location information + returned is valid, but it describes an object that doesn't exist. + You can use the location information for another operation, such as + creating a file or directory. + + vRefNum input: Volume specification. + dirID input: Directory ID. + pathname input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + realVRefNum output: The real volume reference number. + realParID output: The parent directory ID of the specified object. + realName output: The name of the specified object (the case of the + object name may not be the same as the object's + catalog entry on disk - since the Macintosh file + system is not case sensitive, it shouldn't matter). + isDirectory output: True if object is a directory; false if object + is a file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSMakeFSSpecCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetDirItems( + short vRefNum, + long dirID, + ConstStr255Param name, + Boolean getFiles, + Boolean getDirectories, + FSSpecPtr items, + short reqItemCount, + short * actItemCount, + short * itemIndex); + + +/* + The GetDirItems function returns a list of items in the specified + directory in an array of FSSpec records. File, subdirectories, or + both can be returned in the list. + + A noErr result indicates that the items array was filled + (actItemCount == reqItemCount) and there may be additional items + left in the directory. A fnfErr result indicates that the end of + the directory list was found and actItemCount items were actually + found this time. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID + specifies a directory that's the object. + getFiles input: Pass true to have files added to the items list. + getDirectories input: Pass true to have directories added to the + items list. + items input: Pointer to array of FSSpec where the item list + is returned. + reqItemCount input: Maximum number of items to return (the number + of elements in the items array). + actItemCount output: The number of items actually returned. + itemIndex input: The current item index position. Set to 1 to + start with the first item in the directory. + output: The item index position to get the next item. + Pass this value the next time you call + GetDirItems to start where you left off. + + Result Codes + noErr 0 No error, but there are more items + to list + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found, there are no more items + to be listed. + paramErr -50 No default volume or itemIndex was <= 0 + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DeleteDirectoryContents( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The DeleteDirectoryContents function deletes the contents of a directory. + All files and subdirectories in the specified directory are deleted. + If a locked file or directory is encountered, it is unlocked and then + deleted. If any unexpected errors are encountered, + DeleteDirectoryContents quits and returns to the caller. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + fBsyErr -47 File busy, directory not empty, or working directory control block open + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: DeleteDirectory +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +DeleteDirectory( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The DeleteDirectory function deletes a directory and its contents. + All files and subdirectories in the specified directory are deleted. + If a locked file or directory is encountered, it is unlocked and then + deleted. After deleting the directories contents, the directory is + deleted. If any unexpected errors are encountered, DeleteDirectory + quits and returns to the caller. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to directory name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + fBsyErr -47 File busy, directory not empty, or working directory control block open + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: DeleteDirectoryContents +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +CheckObjectLock( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The CheckObjectLock function determines if a file or directory is locked. + If CheckObjectLock returns noErr, then the file or directory + is not locked. If CheckObjectLock returns fLckdErr, the it is locked. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: FSpCheckObjectLock +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCheckObjectLock(const FSSpec * spec); + + +/* + The FSpCheckObjectLock function determines if a file or directory is locked. + If FSpCheckObjectLock returns noErr, then the file or directory + is not locked. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + Also see: CheckObjectLock +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetFileSize( + short vRefNum, + long dirID, + ConstStr255Param fileName, + long * dataSize, + long * rsrcSize); + + +/* + The GetFileSize function returns the logical size of a file's + data and resource fork. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: The name of the file. + dataSize output: The number of bytes in the file's data fork. + rsrcSize output: The number of bytes in the file's resource fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpGetFileSize +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetFileSize( + const FSSpec * spec, + long * dataSize, + long * rsrcSize); + + +/* + The FSpGetFileSize function returns the logical size of a file's + data and resource fork. + + spec input: An FSSpec record specifying the file. + dataSize output: The number of bytes in the file's data fork. + rsrcSize output: The number of bytes in the file's resource fork. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + paramErr -50 No default volume + dirNFErrdirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: GetFileSize +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +BumpDate( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The BumpDate function changes the modification date of a file or + directory to the current date/time. If the modification date is already + equal to the current date/time, then add one second to the + modification date. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpBumpDate +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpBumpDate(const FSSpec * spec); + + +/* + The FSpBumpDate function changes the modification date of a file or + directory to the current date/time. If the modification date is already + equal to the current date/time, then add one second to the + modification date. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: BumpDate +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ChangeCreatorType( + short vRefNum, + long dirID, + ConstStr255Param name, + OSType creator, + OSType fileType); + + +/* + The ChangeCreatorType function changes the creator or file type of a file. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: The name of the file. + creator input: The new creator type or 0x00000000 to leave + the creator type alone. + fileType input: The new file type or 0x00000000 to leave the + file type alone. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + notAFileErr -1302 Name was not a file + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpChangeCreatorType +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpChangeCreatorType( + const FSSpec * spec, + OSType creator, + OSType fileType); + + +/* + The FSpChangeCreatorType function changes the creator or file type of a file. + + spec input: An FSSpec record specifying the file. + creator input: The new creator type or 0x00000000 to leave + the creator type alone. + fileType input: The new file type or 0x00000000 to leave the + file type alone. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + notAFileErr -1302 Name was not a file + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: ChangeCreatorType +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ChangeFDFlags( + short vRefNum, + long dirID, + ConstStr255Param name, + Boolean setBits, + unsigned short flagBits); + + +/* + The ChangeFDFlags function sets or clears Finder Flag bits in the + fdFlags field of a file or directory's FInfo record. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + setBits input: If true, then set the bits specified in flagBits. + If false, then clear the bits specified in flagBits. + flagBits input: The flagBits parameter specifies which Finder Flag + bits to set or clear. If a bit in flagBits is set, + then the same bit in fdFlags is either set or + cleared depending on the state of the setBits + parameter. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpChangeFDFlags +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpChangeFDFlags( + const FSSpec * spec, + Boolean setBits, + unsigned short flagBits); + + +/* + The FSpChangeFDFlags function sets or clears Finder Flag bits in the + fdFlags field of a file or directory's FInfo record. + + spec input: An FSSpec record specifying the object. + setBits input: If true, then set the bits specified in flagBits. + If false, then clear the bits specified in flagBits. + flagBits input: The flagBits parameter specifies which Finder Flag + bits to set or clear. If a bit in flagBits is set, + then the same bit in fdFlags is either set or + cleared depending on the state of the setBits + parameter. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: ChangeFDFlags +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +SetIsInvisible( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The SetIsInvisible function sets the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetIsInvisible, ClearIsInvisible, FSpClearIsInvisible +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetIsInvisible(const FSSpec * spec); + + +/* + The FSpSetIsInvisible function sets the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsInvisible, ClearIsInvisible, FSpClearIsInvisible +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ClearIsInvisible( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The ClearIsInvisible function clears the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsInvisible, FSpSetIsInvisible, FSpClearIsInvisible +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpClearIsInvisible(const FSSpec * spec); + + +/* + The FSpClearIsInvisible function clears the invisible bit in the fdFlags + word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsInvisible, FSpSetIsInvisible, ClearIsInvisible +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +SetNameLocked( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The SetNameLocked function sets the nameLocked bit in the fdFlags word + of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetNameLocked, ClearNameLocked, FSpClearNameLocked +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetNameLocked(const FSSpec * spec); + + +/* + The FSpSetNameLocked function sets the nameLocked bit in the fdFlags word + of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetNameLocked, ClearNameLocked, FSpClearNameLocked +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ClearNameLocked( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The ClearNameLocked function clears the nameLocked bit in the fdFlags + word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetNameLocked, FSpSetNameLocked, FSpClearNameLocked +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpClearNameLocked(const FSSpec * spec); + + +/* + The FSpClearNameLocked function clears the nameLocked bit in the fdFlags + word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetNameLocked, FSpSetNameLocked, ClearNameLocked +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +SetIsStationery( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The SetIsStationery function sets the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetIsStationery, ClearIsStationery, FSpClearIsStationery +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetIsStationery(const FSSpec * spec); + + +/* + The FSpSetIsStationery function sets the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsStationery, ClearIsStationery, FSpClearIsStationery +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ClearIsStationery( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The ClearIsStationery function clears the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsStationery, FSpSetIsStationery, FSpClearIsStationery +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpClearIsStationery(const FSSpec * spec); + + +/* + The FSpClearIsStationery function clears the isStationery bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetIsStationery, FSpSetIsStationery, ClearIsStationery +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +SetHasCustomIcon( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The SetHasCustomIcon function sets the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpSetHasCustomIcon, ClearHasCustomIcon, FSpClearHasCustomIcon +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpSetHasCustomIcon(const FSSpec * spec); + + +/* + The FSpSetHasCustomIcon function sets the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetHasCustomIcon, ClearHasCustomIcon, FSpClearHasCustomIcon +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ClearHasCustomIcon( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The ClearHasCustomIcon function clears the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetHasCustomIcon, FSpSetHasCustomIcon, FSpClearHasCustomIcon +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpClearHasCustomIcon(const FSSpec * spec); + + +/* + The FSpClearHasCustomIcon function clears the hasCustomIcon bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: SetHasCustomIcon, FSpSetHasCustomIcon, ClearHasCustomIcon +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +ClearHasBeenInited( + short vRefNum, + long dirID, + ConstStr255Param name); + + +/* + The ClearHasBeenInited function clears the hasBeenInited bit in the + fdFlags word of the specified file or directory's finder information. + + vRefNum input: Volume specification. + dirID input: Directory ID. + name input: Pointer to object name, or nil when dirID specifies + a directory that's the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpClearHasBeenInited +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpClearHasBeenInited(const FSSpec * spec); + + +/* + The FSpClearHasBeenInited function clears the hasBeenInited bit in the + fdFlags word of the specified file or directory's finder information. + + spec input: An FSSpec record specifying the object. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: ClearHasBeenInited +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +CopyFileMgrAttributes( + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName, + Boolean copyLockBit); + + +/* + The CopyFileMgrAttributes function copies all File Manager attributes + from the source file or directory to the destination file or directory. + If copyLockBit is true, then set the locked state of the destination + to match the source. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Pointer to source object name, or nil when + srcDirID specifies a directory that's the object. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination object name, or nil when + dstDirID specifies a directory that's the object. + copyLockBit input: If true, set the locked state of the destination + to match the source. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: FSpCopyFileMgrAttributes +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCopyFileMgrAttributes( + const FSSpec * srcSpec, + const FSSpec * dstSpec, + Boolean copyLockBit); + + +/* + The FSpCopyFileMgrAttributes function copies all File Manager attributes + from the source file or directory to the destination file or directory. + If copyLockBit is true, then set the locked state of the destination + to match the source. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination object. + copyLockBit input: If true, set the locked state of the destination + to match the source. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + ioErr -36 I/O error + bdNamErr -37 Bad filename + fnfErr -43 File not found + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + paramErr -50 No default volume + dirNFErr -120 Directory not found or incomplete pathname + afpAccessDenied -5000 User does not have the correct access + afpObjectTypeErr -5025 Directory not found or incomplete pathname + + __________ + + See also: CopyFileMgrAttributes +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HOpenAware( + short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short * refNum); + + +/* + The HOpenAware function opens the data fork of a file using deny mode + permissions instead the normal File Manager permissions. If OpenDeny + is not available, then HOpenAware translates the deny modes to the + closest File Manager permissions and tries to open the file with + OpenDF first, and then Open if OpenDF isn't available. By using + HOpenAware with deny mode permissions, a program can be "AppleShare + aware" and fall back on the standard File Manager open calls + automatically. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: The name of the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: FSpOpenAware, HOpenRFAware, FSpOpenRFAware +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpOpenAware( + const FSSpec * spec, + short denyModes, + short * refNum); + + +/* + The FSpOpenAware function opens the data fork of a file using deny mode + permissions instead the normal File Manager permissions. If OpenDeny + is not available, then FSpOpenAware translates the deny modes to the + closest File Manager permissions and tries to open the file with + OpenDF first, and then Open if OpenDF isn't available. By using + FSpOpenAware with deny mode permissions, a program can be "AppleShare + aware" and fall back on the standard File Manager open calls + automatically. + + spec input: An FSSpec record specifying the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: HOpenAware, HOpenRFAware, FSpOpenRFAware +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HOpenRFAware( + short vRefNum, + long dirID, + ConstStr255Param fileName, + short denyModes, + short * refNum); + + +/* + The HOpenRFAware function opens the resource fork of a file using deny + mode permissions instead the normal File Manager permissions. If + OpenRFDeny is not available, then HOpenRFAware translates the deny + modes to the closest File Manager permissions and tries to open the + file with OpenRF. By using HOpenRFAware with deny mode permissions, + a program can be "AppleShare aware" and fall back on the standard + File Manager open calls automatically. + + vRefNum input: Volume specification. + dirID input: Directory ID. + fileName input: The name of the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: HOpenAware, FSpOpenAware, FSpOpenRFAware +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpOpenRFAware( + const FSSpec * spec, + short denyModes, + short * refNum); + + +/* + The FSpOpenRFAware function opens the resource fork of a file using deny + mode permissions instead the normal File Manager permissions. If + OpenRFDeny is not available, then FSpOpenRFAware translates the deny + modes to the closest File Manager permissions and tries to open the + file with OpenRF. By using FSpOpenRFAware with deny mode permissions, + a program can be "AppleShare aware" and fall back on the standard + File Manager open calls automatically. + + spec input: An FSSpec record specifying the file. + denyModes input: The deny modes access under which to open the file. + refNum output: The file reference number of the opened file. + + Result Codes + noErr 0 No error + nsvErr -35 No such volume + tmfoErr -42 Too many files open + fnfErr -43 File not found + wPrErr -44 Volume locked by hardware + fLckdErr -45 File is locked + vLckdErr -46 Volume is locked or read-only + opWrErr -49 File already open for writing + paramErr -50 No default volume + permErr -54 File is already open and cannot be opened using specified deny modes + afpAccessDenied -5000 User does not have the correct access to the file + afpDenyConflict -5006 Requested access permission not possible + + __________ + + See also: HOpenAware, FSpOpenAware, HOpenRFAware +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSReadNoCache( + short refNum, + long * count, + void * buffPtr); + + +/* + The FSReadNoCache function reads any number of bytes from an open file + while asking the file system to bypass its cache mechanism. + + refNum input: The file reference number of an open file. + count input: The number of bytes to read. + output: The number of bytes actually read. + buffPtr input: A pointer to the data buffer into which the bytes are + to be read. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + rfNumErr -51 Bad reference number + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSWriteNoCache +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSWriteNoCache( + short refNum, + long * count, + const void * buffPtr); + + +/* + The FSReadNoCache function writes any number of bytes to an open file + while asking the file system to bypass its cache mechanism. + + refNum input: The file reference number of an open file. + count input: The number of bytes to write to the file. + output: The number of bytes actually written. + buffPtr input: A pointer to the data buffer from which the bytes are + to be written. + + Result Codes + noErr 0 No error + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Disk full + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + rfNumErr -51 Bad reference number + wrPermErr -61 Read/write permission doesnÕt + allow writing + afpAccessDenied -5000 User does not have the correct access to + the file + + __________ + + See also: FSReadNoCache +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSWriteVerify( + short refNum, + long * count, + const void * buffPtr); + + +/* + The FSWriteVerify function writes any number of bytes to an open file + and then verifies that the data was actually written to the device. + + refNum input: The file reference number of an open file. + count input: The number of bytes to write to the file. + output: The number of bytes actually written and verified. + buffPtr input: A pointer to the data buffer from which the bytes are + to be written. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Disk full + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + eofErr -39 Logical end-of-file reached + posErr -40 Attempt to position mark before start + of file + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + rfNumErr -51 Bad reference number + gfpErr -52 Error during GetFPos + wrPermErr -61 Read/write permission doesnÕt + allow writing + memFullErr -108 Not enough room in heap zone to allocate + verify buffer + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +CopyFork( + short srcRefNum, + short dstRefNum, + void * copyBufferPtr, + long copyBufferSize); + + +/* + The CopyFork function copies all data from the source fork to the + destination fork of open file forks and makes sure the destination EOF + is equal to the source EOF. + + srcRefNum input: The source file reference number. + dstRefNum input: The destination file reference number. + copyBufferPtr input: Pointer to buffer to use during copy. The + buffer should be at least 512-bytes minimum. + The larger the buffer, the faster the copy. + copyBufferSize input: The size of the copy buffer. + + Result Codes + noErr 0 No error + readErr Ð19 Driver does not respond to read requests + writErr Ð20 Driver does not respond to write requests + badUnitErr Ð21 Driver reference number does not + match unit table + unitEmptyErr Ð22 Driver reference number specifies a + nil handle in unit table + abortErr Ð27 Request aborted by KillIO + notOpenErr Ð28 Driver not open + dskFulErr -34 Disk full + ioErr Ð36 Data does not match in read-verify mode + fnOpnErr -38 File not open + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Software volume lock + rfNumErr -51 Bad reference number + wrPermErr -61 Read/write permission doesnÕt + allow writing + afpAccessDenied -5000 User does not have the correct access to + the file +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetFileLocation( + short refNum, + short * vRefNum, + long * dirID, + StringPtr fileName); + + +/* + The GetFileLocation function gets the location (volume reference number, + directory ID, and fileName) of an open file. + + refNum input: The file reference number of an open file. + vRefNum output: The volume reference number. + dirID output: The parent directory ID. + fileName input: Points to a buffer (minimum Str63) where the + filename is to be returned or must be nil. + output: The filename. + + Result Codes + noErr 0 No error + nsvErr -35 Specified volume doesnÕt exist + fnOpnErr -38 File not open + rfNumErr -51 Reference number specifies nonexistent + access path + + __________ + + See also: FSpGetFileLocation +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpGetFileLocation( + short refNum, + FSSpec * spec); + + +/* + The FSpGetFileLocation function gets the location of an open file in + an FSSpec record. + + refNum input: The file reference number of an open file. + spec output: FSSpec record containing the file name and location. + + Result Codes + noErr 0 No error + nsvErr -35 Specified volume doesnÕt exist + fnOpnErr -38 File not open + rfNumErr -51 Reference number specifies nonexistent + access path + + __________ + + See also: GetFileLocation +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +CopyDirectoryAccess( + short srcVRefNum, + long srcDirID, + ConstStr255Param srcName, + short dstVRefNum, + long dstDirID, + ConstStr255Param dstName); + + +/* + The CopyDirectoryAccess function copies the AFP directory access + privileges from one directory to another. Both directories must be on + the same file server, but not necessarily on the same server volume. + + srcVRefNum input: Source volume specification. + srcDirID input: Source directory ID. + srcName input: Pointer to source directory name, or nil when + srcDirID specifies the directory. + dstVRefNum input: Destination volume specification. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination directory name, or nil when + dstDirID specifies the directory. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Volume doesn't support this function + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + See also: FSpCopyDirectoryAccess +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpCopyDirectoryAccess( + const FSSpec * srcSpec, + const FSSpec * dstSpec); + + +/* + The FSpCopyDirectoryAccess function copies the AFP directory access + privileges from one directory to another. Both directories must be on + the same file server, but not necessarily on the same server volume. + + srcSpec input: An FSSpec record specifying the source directory. + dstSpec input: An FSSpec record specifying the destination directory. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + fnfErr -43 Directory not found + vLckdErr -46 Volume is locked or read-only + paramErr -50 Volume doesn't support this function + afpAccessDenied -5000 User does not have the correct access + to the directory + afpObjectTypeErr -5025 Object is a file, not a directory + + __________ + + See also: CopyDirectoryAccess +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +HMoveRenameCompat( + short vRefNum, + long srcDirID, + ConstStr255Param srcName, + long dstDirID, + ConstStr255Param dstpathName, + ConstStr255Param copyName); + + +/* + The HMoveRenameCompat function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + volume. This routine works even if the volume doesn't support MoveRename. + + vRefNum input: Volume specification. + srcDirID input: Source directory ID. + srcName input: The source object name. + dstDirID input: Destination directory ID. + dstName input: Pointer to destination directory name, or + nil when dstDirID specifies a directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 Volume not found + ioErr -36 I/O error + bdNamErr -37 Bad filename or attempt to move into + a file + fnfErr -43 Source file or directory not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 File busy, directory not empty, or + working directory control block open + dupFNErr -48 Destination already exists + paramErr -50 Volume doesn't support this function, + no default volume, or source and + volOfflinErr -53 Volume is offline + fsRnErr -59 Problem during rename + dirNFErr -120 Directory not found or incomplete pathname + badMovErr -122 Attempted to move directory into + offspring + wrgVolTypErr -123 Not an HFS volume (it's a MFS volume) + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 The user does not have the right to + move the file or directory + afpObjectTypeErr -5025 Directory not found or incomplete pathname + afpSameObjectErr -5038 Source and destination files are the same + + __________ + + See also: FSpMoveRenameCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +FSpMoveRenameCompat( + const FSSpec * srcSpec, + const FSSpec * dstSpec, + ConstStr255Param copyName); + + +/* + The FSpMoveRenameCompat function moves a file or directory and optionally + renames it. The source and destination locations must be on the same + volume. This routine works even if the volume doesn't support MoveRename. + + srcSpec input: An FSSpec record specifying the source object. + dstSpec input: An FSSpec record specifying the destination + directory. + copyName input: Points to the new name if the object is to be + renamed or nil if the object isn't to be renamed. + + Result Codes + noErr 0 No error + dirFulErr -33 File directory full + dskFulErr -34 Disk is full + nsvErr -35 Volume not found + ioErr -36 I/O error + bdNamErr -37 Bad filename or attempt to move into + a file + fnfErr -43 Source file or directory not found + wPrErr -44 Hardware volume lock + fLckdErr -45 File is locked + vLckdErr -46 Destination volume is read-only + fBsyErr -47 File busy, directory not empty, or + working directory control block open + dupFNErr -48 Destination already exists + paramErr -50 Volume doesn't support this function, + no default volume, or source and + volOfflinErr -53 Volume is offline + fsRnErr -59 Problem during rename + dirNFErr -120 Directory not found or incomplete pathname + badMovErr -122 Attempted to move directory into + offspring + wrgVolTypErr -123 Not an HFS volume (it's a MFS volume) + notAFileErr -1302 The pathname is nil, the pathname + is empty, or the pathname cannot refer + to a filename + diffVolErr -1303 Files on different volumes + afpAccessDenied -5000 The user does not have the right to + move the file or directory + afpObjectTypeErr -5025 Directory not found or incomplete pathname + afpSameObjectErr -5038 Source and destination files are the same + + __________ + + See also: HMoveRenameCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +BuildAFPVolMountInfo( + short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str31 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + AFPVolMountInfoPtr * afpInfoPtr); + + +/* + The BuildAFPVolMountInfo function allocates and initializes the fields + of an AFPVolMountInfo record before using that record to call + the VolumeMount function. + + flags input: The AFP mounting flags. 0 = normal mount; + set bit 0 to inhibit greeting messages. + nbpInterval input: The interval used for VolumeMount's + NBP Lookup call. 7 is a good choice. + nbpCount input: The retry count used for VolumeMount's + NBP Lookup call. 5 is a good choice. + uamType input: The user authentication method to use. + zoneName input: The AppleTalk zone name of the server. + serverName input: The AFP server name. + volName input: The AFP volume name. + userName input: The user name (zero length Pascal string for + guest). + userPassWord input: The user password (zero length Pascal string + if no user password) + volPassWord input: The volume password (zero length Pascal string + if no volume password) + afpInfoPtr output: A pointer to the newly created and initialized + AFPVolMountInfo record. If the function fails to + create an AFPVolMountInfo record, it sets + afpInfoPtr to NULL and the function result is + memFullErr. Your program is responsible + for disposing of this pointer when it is finished + with it. + + Result Codes + noErr 0 No error + memFullErr -108 memory full error + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + RetrieveAFPVolMountInfo, BuildAFPXVolMountInfo, + RetrieveAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +RetrieveAFPVolMountInfo( + AFPVolMountInfoPtr afpInfoPtr, + short * flags, + short * uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName); + + +/* + The RetrieveAFPVolMountInfo function retrieves the AFP mounting + information returned in an AFPVolMountInfo record by the + GetVolMountInfo function. + + afpInfoPtr input: Pointer to AFPVolMountInfo record that contains + the AFP mounting information. + flags output: The AFP mounting flags. + uamType output: The user authentication method used. + zoneName output: The AppleTalk zone name of the server. + serverName output: The AFP server name. + volName output: The AFP volume name. + userName output: The user name (zero length Pascal string for + guest). + + Result Codes + noErr 0 No error + paramErr -50 media field in AFP mounting information + was not AppleShareMediaType + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + BuildAFPVolMountInfo, BuildAFPXVolMountInfo, + RetrieveAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +BuildAFPXVolMountInfo( + short flags, + char nbpInterval, + char nbpCount, + short uamType, + Str32 zoneName, + Str31 serverName, + Str27 volName, + Str31 userName, + Str8 userPassword, + Str8 volPassword, + Str32 uamName, + unsigned long alternateAddressLength, + void * alternateAddress, + AFPXVolMountInfoPtr * afpXInfoPtr); + + +/* + The BuildAFPXVolMountInfo function allocates and initializes the fields + of an AFPXVolMountInfo record before using that record to call + the VolumeMount function. + + flags input: The AFP mounting flags. + nbpInterval input: The interval used for VolumeMount's + NBP Lookup call. 7 is a good choice. + nbpCount input: The retry count used for VolumeMount's + NBP Lookup call. 5 is a good choice. + uamType input: The user authentication method to use. + zoneName input: The AppleTalk zone name of the server. + serverName input: The AFP server name. + volName input: The AFP volume name. + userName input: The user name (zero length Pascal string + for guest). + userPassWord input: The user password (zero length Pascal + string if no user password) + volPassWord input: The volume password (zero length Pascal + string if no volume password) + uamName input: The User Authentication Method name. + alternateAddressLength input: Length of alternateAddress data. + alternateAddress input The AFPAlternateAddress (variable length) + afpXInfoPtr output: A pointer to the newly created and + initialized AFPVolMountInfo record. + If the function fails to create an + AFPVolMountInfo record, it sets + afpInfoPtr to NULL and the function + result is memFullErr. Your program is + responsible for disposing of this pointer + when it is finished with it. + + Result Codes + noErr 0 No error + memFullErr -108 memory full error + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + BuildAFPVolMountInfo, RetrieveAFPVolMountInfo, + RetrieveAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +RetrieveAFPXVolMountInfo( + AFPXVolMountInfoPtr afpXInfoPtr, + short * flags, + short * uamType, + StringPtr zoneName, + StringPtr serverName, + StringPtr volName, + StringPtr userName, + StringPtr uamName, + unsigned long * alternateAddressLength, + AFPAlternateAddress ** alternateAddress); + + +/* + The RetrieveAFPXVolMountInfo function retrieves the AFP mounting + information returned in an AFPXVolMountInfo record by the + GetVolMountInfo function. + + afpXInfoPtr input: Pointer to AFPXVolMountInfo record that + contains the AFP mounting information. + flags output: The AFP mounting flags. + uamType output: The user authentication method used. + zoneName output: The AppleTalk zone name of the server. + serverName output: The AFP server name. + volName output: The AFP volume name. + userName output: The user name (zero length Pascal + string for guest). + uamName output: The User Authentication Method name. + alternateAddressLength output: Length of alternateAddress data returned. + alternateAddress: output: A pointer to the newly created and + AFPAlternateAddress record (a variable + length record). If the function fails to + create an AFPAlternateAddress record, + it sets alternateAddress to NULL and the + function result is memFullErr. Your + program is responsible for disposing of + this pointer when it is finished with it. + + Result Codes + noErr 0 No error + paramErr -50 media field in AFP mounting information + was not AppleShareMediaType + memFullErr -108 memory full error + + __________ + + Also see: GetVolMountInfoSize, GetVolMountInfo, VolumeMount, + BuildAFPVolMountInfo, RetrieveAFXVolMountInfo, + BuildAFPXVolMountInfo +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +GetUGEntries( + short objType, + UGEntryPtr entries, + long reqEntryCount, + long * actEntryCount, + long * objID); + + +/* + The GetUGEntries functions retrieves a list of user or group entries + from the local file server. + + objType input: The object type: -1 = group; 0 = user + UGEntries input: Pointer to array of UGEntry records where the list + is returned. + reqEntryCount input: The number of elements in the UGEntries array. + actEntryCount output: The number of entries returned. + objID input: The current index position. Set to 0 to start with + the first entry. + output: The index position to get the next entry. Pass this + value the next time you call GetUGEntries to start + where you left off. + + Result Codes + noErr 0 No error + fnfErr -43 No more users or groups + paramErr -50 Function not supported; or, ioObjID is + negative + + __________ + + Also see: GetUGEntry +*/ + +/*****************************************************************************/ + + + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MOREFILESEXTRAS__ */ + diff --git a/src/osx/carbon/morefile/Optimization.h b/src/osx/carbon/morefile/Optimization.h new file mode 100644 index 0000000000..d2673f9304 --- /dev/null +++ b/src/osx/carbon/morefile/Optimization.h @@ -0,0 +1,109 @@ +/* + File: Optimization.h + + Contains: Defines that let you make MoreFiles code more efficient. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <1> 2/7/01 JL first checked in +*/ + +/* + The Optimization changes to MoreFiles source and header files, along with + this file and OptimizationEnd.h, let you optimize the code produced + by MoreFiles in several ways. + + 1 -- MoreFiles contains extra code so that many routines can run under + Mac OS systems back to System 6. If your program requires a specific + version of Mac OS and your program checks for that version before + calling MoreFiles routines, then you can remove a lot of compatibility + code by defining one of the following to 1: + + __MACOSSEVENFIVEONEORLATER // assume Mac OS 7.5.1 or later + __MACOSSEVENFIVEORLATER // assume Mac OS 7.5 or later + __MACOSSEVENORLATER // assume Mac OS 7.0 or later + + If you're compiling 68K code, the default is to include all compatibility code. + If you're compiling PowerPC code (TARGET_RT_MAC_CFM), the default is __MACOSSEVENORLATER + If you're compiling for Carbon code (TARGET_API_MAC_CARBON), the default is __MACOSSEVENFIVEONEORLATER + + 2 -- You may disable Pascal calling conventions in all MoreFiles routines + except for system callbacks that require Pascal calling conventions. + This will make 68K C programs both smaller and faster. + (PowerPC compilers ignore pascal calling conventions.) + Just define __WANTPASCALELIMINATION to be 1 to turn this optimization on + when building MoreFiles for use from C programs (you'll need to keep + Pascal calling conventions when linking MoreFiles routines with Pascal + programs). + + 3 -- If Metrowerks compiler is used, "#pragma internal on" may help produce + better code. However, this option can also cause problems if you're + trying to build MoreFiles as a shared library, so it is by default not used. + Just define __USEPRAGMAINTERNAL to be 1 to turn this optimization on. + + Original changes supplied by Fabrizio Oddone +*/ + +#include + +// if we're compiling for Carbon, then we're running on Mac OS 8.1 or later +#ifndef __MACOSSEVENFIVEONEORLATER + #define __MACOSSEVENFIVEONEORLATER TARGET_API_MAC_CARBON +#endif + +#ifndef __MACOSSEVENFIVEORLATER + #define __MACOSSEVENFIVEORLATER __MACOSSEVENFIVEONEORLATER +#endif + +#ifndef __MACOSSEVENORLATER + #if TARGET_RT_MAC_CFM + #define __MACOSSEVENORLATER 1 + #else + #define __MACOSSEVENORLATER __MACOSSEVENFIVEORLATER + #endif +#endif + + +#ifndef __WANTPASCALELIMINATION + #define __WANTPASCALELIMINATION 0 +#endif + +#if __WANTPASCALELIMINATION + #define pascal +#endif + + +#ifndef __USEPRAGMAINTERNAL + #define __USEPRAGMAINTERNAL 0 +#endif + +#if __USEPRAGMAINTERNAL + #if defined(__MWERKS__) + #pragma internal on + #endif +#endif diff --git a/src/osx/carbon/morefile/OptimizationEnd.h b/src/osx/carbon/morefile/OptimizationEnd.h new file mode 100644 index 0000000000..10ed02507f --- /dev/null +++ b/src/osx/carbon/morefile/OptimizationEnd.h @@ -0,0 +1,56 @@ +/* + File: OptimizationEnd.h + + Contains: Defines that let you make MoreFiles code more efficient. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <1> 2/7/01 JL first checked in +*/ + +/* + The Optimization changes to MoreFiles source and header files, along with + this file and Optimization.h, let you optimize the code produced by MoreFiles + in several ways. + + Original changes supplied by Fabrizio Oddone +*/ + + +#if __USEPRAGMAINTERNAL + #if defined(__MWERKS__) + #pragma internal reset + #endif +#endif + + +#if __WANTPASCALELIMINATION + #ifndef __COMPILINGMOREFILES + #undef pascal + #endif +#endif diff --git a/src/osx/carbon/morefile/Search.c b/src/osx/carbon/morefile/Search.c new file mode 100644 index 0000000000..d03154a5ee --- /dev/null +++ b/src/osx/carbon/morefile/Search.c @@ -0,0 +1,1300 @@ +/* + File: Search.c + + Contains: IndexedSearch and the PBCatSearch compatibility function. + + Version: MoreFiles + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. + + File Ownership: + + DRI: Jim Luther + + Other Contact: Apple Macintosh Developer Technical Support + + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <2> 2/7/01 JL Added standard header. Updated names of includes. Updated + various routines to use new calling convention of the + MoreFilesExtras accessor functions. Added TARGET_API_MAC_CARBON + conditional checks around TimeOutTask. + <1> 12/06/99 JL MoreFiles 1.5. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define __COMPILINGMOREFILES + +#include "MoreFiles.h" +#include "MoreFilesExtras.h" + +#include "Search.h" + +/*****************************************************************************/ + +enum +{ + /* Number of LevelRecs to add each time the searchStack is grown */ + /* 20 levels is probably more than reasonable for most volumes. */ + /* If more are needed, they are allocated 20 levels at a time. */ + kAdditionalLevelRecs = 20 +}; + +/*****************************************************************************/ + +/* +** LevelRecs are used to store the directory ID and index whenever +** IndexedSearch needs to either scan a sub-directory, or return control +** to the caller because the call has timed out or the number of +** matches requested has been found. LevelRecs are stored in an array +** used as a stack. +*/ +struct LevelRec +{ + long dirModDate; /* for detecting most (but not all) catalog changes */ + long dirID; + short index; +}; +typedef struct LevelRec LevelRec; +typedef LevelRec *LevelRecPtr, **LevelRecHandle; + + +/* +** SearchPositionRec is my version of a CatPositionRec. It holds the +** information I need to resuming searching. +*/ +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#endif +struct SearchPositionRec +{ + long initialize; /* Goofy checksum of volume information used to make */ + /* sure we're resuming a search on the same volume. */ + unsigned short stackDepth; /* Current depth on searchStack. */ + short priv[11]; /* For future use... */ +}; +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#endif +typedef struct SearchPositionRec SearchPositionRec; +typedef SearchPositionRec *SearchPositionRecPtr; + + +/* +** ExtendedTMTask is a TMTask record extended to hold the timer flag. +*/ +#if PRAGMA_STRUCT_ALIGN +#pragma options align=mac68k +#endif +struct ExtendedTMTask +{ + TMTask theTask; + Boolean stopSearch; /* the Time Mgr task will set stopSearch to */ + /* true when the timer expires */ +}; +#if PRAGMA_STRUCT_ALIGN +#pragma options align=reset +#endif +typedef struct ExtendedTMTask ExtendedTMTask; +typedef ExtendedTMTask *ExtendedTMTaskPtr; + +/*****************************************************************************/ + +static OSErr CheckVol(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum, + long *volID); + +static OSErr CheckStack(unsigned short stackDepth, + LevelRecHandle searchStack, + Size *searchStackSize); + +static OSErr VerifyUserPB(CSParamPtr userPB, + Boolean *includeFiles, + Boolean *includeDirs, + Boolean *includeNames); + +static Boolean IsSubString(ConstStr255Param aStringPtr, + ConstStr255Param subStringPtr); + +static Boolean CompareMasked(const long *data1, + const long *data2, + const long *mask, + short longsToCompare); + +static void CheckForMatches(CInfoPBPtr cPB, + CSParamPtr userPB, + const Str63 matchName, + Boolean includeFiles, + Boolean includeDirs); + +#if __WANTPASCALELIMINATION +#undef pascal +#endif + +#if TARGET_RT_MAC_CFM || TARGET_API_MAC_CARBON + +static pascal void TimeOutTask(TMTaskPtr tmTaskPtr); + +#else + +static pascal TMTaskPtr GetTMTaskPtr(void); + +static void TimeOutTask(void); + +#endif + +#if __WANTPASCALELIMINATION +#define pascal +#endif + +static long GetDirModDate(short vRefNum, + long dirID); + +/*****************************************************************************/ + +/* +** CheckVol gets the volume's real vRefNum and builds a volID. The volID +** is used to help insure that calls to resume searching with IndexedSearch +** are to the same volume as the last call to IndexedSearch. +*/ +static OSErr CheckVol(ConstStr255Param pathname, + short vRefNum, + short *realVRefNum, + long *volID) +{ + HParamBlockRec pb; + OSErr error; + + error = GetVolumeInfoNoName(pathname, vRefNum, &pb); + if ( error == noErr ) + { + /* Return the real vRefNum */ + *realVRefNum = pb.volumeParam.ioVRefNum; + + /* Add together a bunch of things that aren't supposed to change on */ + /* a mounted volume that's being searched and that should come up with */ + /* a fairly unique number */ + *volID = pb.volumeParam.ioVCrDate + + pb.volumeParam.ioVRefNum + + pb.volumeParam.ioVNmAlBlks + + pb.volumeParam.ioVAlBlkSiz + + pb.volumeParam.ioVFSID; + } + return ( error ); +} + +/*****************************************************************************/ + +/* +** CheckStack checks the size of the search stack (array) to see if there's +** room to push another LevelRec. If not, CheckStack grows the stack by +** another kAdditionalLevelRecs elements. +*/ +static OSErr CheckStack(unsigned short stackDepth, + LevelRecHandle searchStack, + Size *searchStackSize) +{ + OSErr result; + + if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) ) + { + /* Time to grow stack */ + SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec))); + result = MemError(); /* should be noErr */ + *searchStackSize = GetHandleSize((Handle)searchStack); + } + else + { + result = noErr; + } + + return ( result ); +} + +/*****************************************************************************/ + +/* +** VerifyUserPB makes sure the parameter block passed to IndexedSearch has +** valid parameters. By making this check once, we don't have to worry about +** things like NULL pointers, strings being too long, etc. +** VerifyUserPB also determines if the search includes files and/or +** directories, and determines if a full or partial name search was requested. +*/ +static OSErr VerifyUserPB(CSParamPtr userPB, + Boolean *includeFiles, + Boolean *includeDirs, + Boolean *includeNames) +{ + CInfoPBPtr searchInfo1; + CInfoPBPtr searchInfo2; + + searchInfo1 = userPB->ioSearchInfo1; + searchInfo2 = userPB->ioSearchInfo2; + + /* ioMatchPtr cannot be NULL */ + if ( userPB->ioMatchPtr == NULL ) + { + goto ParamErrExit; + } + + /* ioSearchInfo1 cannot be NULL */ + if ( searchInfo1 == NULL ) + { + goto ParamErrExit; + } + + /* If any bits except partialName, fullName, or negate are set, then */ + /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required */ + if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) && + ( searchInfo2 == NULL )) + { + goto ParamErrExit; + } + + *includeFiles = false; + *includeDirs = false; + *includeNames = false; + + if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 ) + { + /* If any kind of name matching is requested, then ioNamePtr in */ + /* ioSearchInfo1 cannot be NULL or a zero-length string */ + if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) || + (searchInfo1->hFileInfo.ioNamePtr[0] == 0) || + (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) ) + { + goto ParamErrExit; + } + + *includeNames = true; + } + + if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 ) + { + /* The only attributes you can search on are the directory flag */ + /* and the locked flag. */ + if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(kioFlAttribDirMask | kioFlAttribLockedMask)) != 0 ) + { + goto ParamErrExit; + } + + /* interested in the directory bit? */ + if ( (searchInfo2->hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* yes, so do they want just directories or just files? */ + if ( (searchInfo1->hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + *includeDirs = true; + } + else + { + *includeFiles = true; + } + } + else + { + /* no interest in directory bit - get both files and directories */ + *includeDirs = true; + *includeFiles = true; + } + } + else + { + /* no attribute checking - get both files and directories */ + *includeDirs = true; + *includeFiles = true; + } + + /* If directories are included in the search, */ + /* then the locked attribute cannot be requested. */ + if ( *includeDirs && + ((userPB->ioSearchBits & fsSBFlAttrib) != 0) && + ((searchInfo2->hFileInfo.ioFlAttrib & kioFlAttribLockedMask) != 0) ) + { + goto ParamErrExit; + } + + /* If files are included in the search, then there cannot be */ + /* a search on the number of files. */ + if ( *includeFiles && + ((userPB->ioSearchBits & fsSBDrNmFls) != 0) ) + { + goto ParamErrExit; + } + + /* If directories are included in the search, then there cannot */ + /* be a search on file lengths. */ + if ( *includeDirs && + ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) ) + { + goto ParamErrExit; + } + + return ( noErr ); + +ParamErrExit: + return ( paramErr ); +} + +/*****************************************************************************/ + +/* +** IsSubString checks to see if a string is a substring of another string. +** Both input strings have already been converted to all uppercase using +** UprString (the same non-international call the File Manager uses). +*/ +static Boolean IsSubString(ConstStr255Param aStringPtr, + ConstStr255Param subStringPtr) +{ + short strLength; /* length of string */ + short subStrLength; /* length of subString */ + Boolean found; /* result of test */ + short index; /* current index into string */ + + found = false; + strLength = aStringPtr[0]; + subStrLength = subStringPtr[0]; + + if ( subStrLength <= strLength) + { + register short count; /* search counter */ + register short strIndex; /* running index into string */ + register short subStrIndex; /* running index into subString */ + + /* start looking at first character */ + index = 1; + + /* continue looking until remaining string is shorter than substring */ + count = strLength - subStrLength + 1; + + do + { + strIndex = index; /* start string index at index */ + subStrIndex = 1; /* start subString index at 1 */ + + while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) ) + { + if ( subStrIndex == subStrLength ) + { + /* all characters in subString were found */ + found = true; + } + else + { + /* check next character of substring against next character of string */ + ++subStrIndex; + ++strIndex; + } + } + + if ( !found ) + { + /* start substring search again at next string character */ + ++index; + --count; + } + } while ( count != 0 && (!found) ); + } + + return ( found ); +} + +/*****************************************************************************/ + +/* +** CompareMasked does a bitwise comparison with mask on 1 or more longs. +** data1 and data2 are first exclusive-ORed together resulting with bits set +** where they are different. That value is then ANDed with the mask resulting +** with bits set if the test fails. true is returned if the tests pass. +*/ +static Boolean CompareMasked(const long *data1, + const long *data2, + const long *mask, + short longsToCompare) +{ + Boolean result = true; + + while ( (longsToCompare != 0) && (result == true) ) + { + /* (*data1 ^ *data2) = bits that are different, so... */ + /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */ + + if ( ((*data1 ^ *data2) & *mask) != 0 ) + result = false; + + ++data1; + ++data2; + ++mask; + --longsToCompare; + } + + return ( result ); +} + +/*****************************************************************************/ + +/* +** Check for matches compares the search criteria in userPB to the file +** system object in cPB. If there's a match, then the information in cPB is +** is added to the match array and the actual match count is incremented. +*/ +static void CheckForMatches(CInfoPBPtr cPB, + CSParamPtr userPB, + const Str63 matchName, + Boolean includeFiles, + Boolean includeDirs) +{ + long searchBits; + CInfoPBPtr searchInfo1; + CInfoPBPtr searchInfo2; + Str63 itemName; /* copy of object's name for partial name matching */ + Boolean foundMatch; + + foundMatch = false; /* default to no match */ + + searchBits = userPB->ioSearchBits; + searchInfo1 = userPB->ioSearchInfo1; + searchInfo2 = userPB->ioSearchInfo2; + + /* Into the if statements that go on forever... */ + + if ( (cPB->hFileInfo.ioFlAttrib & kioFlAttribDirMask) == 0 ) + { + if (!includeFiles) + { + goto Failed; + } + } + else + { + if (!includeDirs) + { + goto Failed; + } + } + + if ( (searchBits & fsSBPartialName) != 0 ) + { + if ( (cPB->hFileInfo.ioNamePtr[0] > 0) && + (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) ) + { + /* Make uppercase copy of object name */ + BlockMoveData(cPB->hFileInfo.ioNamePtr, + itemName, + cPB->hFileInfo.ioNamePtr[0] + 1); + /* Use the same non-international call the File Manager uses */ + UpperString(itemName, true); + } + else + { + goto Failed; + } + + { + if ( !IsSubString(itemName, matchName) ) + { + goto Failed; + } + else if ( searchBits == fsSBPartialName ) + { + /* optimize for name matching only since it is most common way to search */ + goto Hit; + } + } + } + + if ( (searchBits & fsSBFullName) != 0 ) + { + /* Use the same non-international call the File Manager uses */ + if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) ) + { + goto Failed; + } + else if ( searchBits == fsSBFullName ) + { + /* optimize for name matching only since it is most common way to search */ + goto Hit; + } + } + + if ( (searchBits & fsSBFlParID) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) || + ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlAttrib) != 0 ) + { + if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) & + searchInfo2->hFileInfo.ioFlAttrib) != 0 ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBDrNmFls) != 0 ) + { + if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) || + ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlFndrInfo) != 0 ) /* fsSBFlFndrInfo is same as fsSBDrUsrWds */ + { + if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo), + (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo), + (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo), + sizeof(FInfo) / sizeof(long)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlXFndrInfo) != 0 ) /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */ + { + if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo), + (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo), + (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo), + sizeof(FXInfo) / sizeof(long)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlLgLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlPyLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlRLgLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlRPyLen) != 0 ) + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) || + ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlCrDat) != 0 ) /* fsSBFlCrDat is same as fsSBDrCrDat */ + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) || + ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlMdDat) != 0 ) /* fsSBFlMdDat is same as fsSBDrMdDat */ + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) || + ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) ) + { + goto Failed; + } + } + + if ( (searchBits & fsSBFlBkDat) != 0 ) /* fsSBFlBkDat is same as fsSBDrBkDat */ + { + if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) || + ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) ) + { + goto Failed; + } + } + + /* Hey, we passed all of the tests! */ + +Hit: + foundMatch = true; + +/* foundMatch is false if code jumps to Failed */ +Failed: + /* Do we reverse our findings? */ + if ( (searchBits & fsSBNegate) != 0 ) + { + foundMatch = !foundMatch; /* matches are not, not matches are */ + } + + if ( foundMatch ) + { + + /* Move the match into the match buffer */ + userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum; + userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID; + if ( cPB->hFileInfo.ioNamePtr[0] > 63 ) + { + cPB->hFileInfo.ioNamePtr[0] = 63; + } + BlockMoveData(cPB->hFileInfo.ioNamePtr, + userPB->ioMatchPtr[userPB->ioActMatchCount].name, + cPB->hFileInfo.ioNamePtr[0] + 1); + + /* increment the actual count */ + ++(userPB->ioActMatchCount); + } +} + +/*****************************************************************************/ + +/* +** TimeOutTask is executed when the timer goes off. It simply sets the +** stopSearch field to true. After each object is found and possibly added +** to the matches buffer, stopSearch is checked to see if the search should +** continue. +*/ + +#if __WANTPASCALELIMINATION +#undef pascal +#endif + +#if TARGET_RT_MAC_CFM || TARGET_API_MAC_CARBON + +static pascal void TimeOutTask(TMTaskPtr tmTaskPtr) +{ + ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true; +} + +#else + +static pascal TMTaskPtr GetTMTaskPtr(void) + ONEWORDINLINE(0x2e89); /* MOVE.L A1,(SP) */ + +static void TimeOutTask(void) +{ + ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true; +} + +#endif + +#if __WANTPASCALELIMINATION +#define pascal +#endif + +/*****************************************************************************/ + +/* +** GetDirModDate returns the modification date of a directory. If there is +** an error getting the modification date, -1 is returned to indicate +** something went wrong. +*/ +static long GetDirModDate(short vRefNum, + long dirID) +{ + CInfoPBRec pb; + Str31 tempName; + long modDate; + + /* Protection against File Sharing problem */ + tempName[0] = 0; + pb.dirInfo.ioNamePtr = tempName; + pb.dirInfo.ioVRefNum = vRefNum; + pb.dirInfo.ioDrDirID = dirID; + pb.dirInfo.ioFDirIndex = -1; /* use ioDrDirID */ + + if ( PBGetCatInfoSync(&pb) == noErr ) + { + modDate = pb.dirInfo.ioDrMdDat; + } + else + { + modDate = -1; + } + + return ( modDate ); +} + +/*****************************************************************************/ + +pascal OSErr IndexedSearch(CSParamPtr pb, + long dirID) +{ + static LevelRecHandle searchStack = NULL; /* static handle to LevelRec stack */ + static Size searchStackSize = 0; /* size of static handle */ + SearchPositionRecPtr catPosition; + long modDate; + short index = -1 ; + ExtendedTMTask timerTask; + OSErr result; + short realVRefNum; + Str63 itemName; + CInfoPBRec cPB; + long tempLong; + Boolean includeFiles; + Boolean includeDirs; + Boolean includeNames; + Str63 upperName; + + timerTask.stopSearch = false; /* don't stop yet! */ + + /* If request has a timeout, install a Time Manager task. */ + if ( pb->ioSearchTime != 0 ) + { + /* Start timer */ + timerTask.theTask.tmAddr = NewTimerUPP(TimeOutTask); + InsTime((QElemPtr)&(timerTask.theTask)); + PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime); + } + + /* Check the parameter block passed for things that we don't want to assume */ + /* are OK later in the code. For example, make sure pointers to data structures */ + /* and buffers are not NULL. And while we're in there, see if the request */ + /* specified searching for files, directories, or both, and see if the search */ + /* was by full or partial name. */ + result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames); + if ( result == noErr ) + { + pb->ioActMatchCount = 0; /* no matches yet */ + + if ( includeNames ) + { + /* The search includes seach by full or partial name. */ + /* Make an upper case copy of the match string to pass to */ + /* CheckForMatches. */ + BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr, + upperName, + pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1); + /* Use the same non-international call the File Manager uses */ + UpperString(upperName, true); + } + + /* Prevent casting to my type throughout code */ + catPosition = (SearchPositionRecPtr)&pb->ioCatPosition; + + /* Create searchStack first time called */ + if ( searchStack == NULL ) + { + searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec)); + } + + /* Make sure searchStack really exists */ + if ( searchStack != NULL ) + { + searchStackSize = GetHandleSize((Handle)searchStack); + + /* See if the search is a new search or a resumed search. */ + if ( catPosition->initialize == 0 ) + { + /* New search. */ + + /* Get the real vRefNum and fill in catPosition->initialize. */ + result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize); + if ( result == noErr ) + { + /* clear searchStack */ + catPosition->stackDepth = 0; + + /* use dirID parameter passed and... */ + index = -1; /* start with the passed directory itself! */ + } + } + else + { + /* We're resuming a search. */ + + /* Get the real vRefNum and make sure catPosition->initialize is valid. */ + result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong); + if ( result == noErr ) + { + /* Make sure the resumed search is to the same volume! */ + if ( catPosition->initialize == tempLong ) + { + /* For resume, catPosition->stackDepth > 0 */ + if ( catPosition->stackDepth > 0 ) + { + /* Position catPosition->stackDepth to access last saved level */ + --(catPosition->stackDepth); + + /* Get the dirID and index for the next item */ + dirID = (*searchStack)[catPosition->stackDepth].dirID; + index = (*searchStack)[catPosition->stackDepth].index; + + /* Check the dir's mod date against the saved mode date on our "stack" */ + modDate = GetDirModDate(realVRefNum, dirID); + if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate ) + { + result = catChangedErr; + } + } + else + { + /* Invalid catPosition record was passed */ + result = paramErr; + } + } + else + { + /* The volume is not the same */ + result = catChangedErr; + } + } + } + + if ( result == noErr ) + { + /* ioNamePtr and ioVRefNum only need to be set up once. */ + cPB.hFileInfo.ioNamePtr = itemName; + cPB.hFileInfo.ioVRefNum = realVRefNum; + + /* + ** Here's the loop that: + ** Finds the next item on the volume. + ** If noErr, calls the code to check for matches and add matches + ** to the match buffer. + ** Sets up dirID and index for to find the next item on the volume. + ** + ** The looping ends when: + ** (a) an unexpected error is returned by PBGetCatInfo. All that + ** is expected is noErr and fnfErr (after the last item in a + ** directory is found). + ** (b) the caller specified a timeout and our Time Manager task + ** has fired. + ** (c) the number of matches requested by the caller has been found. + ** (d) the last item on the volume was found. + */ + do + { + /* get the next item */ + cPB.hFileInfo.ioFDirIndex = index; + cPB.hFileInfo.ioDirID = dirID; + result = PBGetCatInfoSync(&cPB); + if ( index != -1 ) + { + if ( result == noErr ) + { + /* We found something */ + + CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs); + + ++index; + if ( (cPB.dirInfo.ioFlAttrib & kioFlAttribDirMask) != 0 ) + { + /* It's a directory */ + + result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize); + if ( result == noErr ) + { + /* Save the current state on the searchStack */ + /* when we come back, this is where we'll start */ + (*searchStack)[catPosition->stackDepth].dirID = dirID; + (*searchStack)[catPosition->stackDepth].index = index; + (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID); + + /* position catPosition->stackDepth for next saved level */ + ++(catPosition->stackDepth); + + /* The next item to get is the 1st item in the child directory */ + dirID = cPB.dirInfo.ioDrDirID; + index = 1; + } + } + /* else do nothing for files */ + } + else + { + /* End of directory found (or we had some error and that */ + /* means we have to drop out of this directory). */ + /* Restore last thing put on stack and */ + /* see if we need to continue or quit. */ + if ( catPosition->stackDepth > 0 ) + { + /* position catPosition->stackDepth to access last saved level */ + --(catPosition->stackDepth); + + dirID = (*searchStack)[catPosition->stackDepth].dirID; + index = (*searchStack)[catPosition->stackDepth].index; + + /* Check the dir's mod date against the saved mode date on our "stack" */ + modDate = GetDirModDate(realVRefNum, dirID); + if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate ) + { + result = catChangedErr; + } + else + { + /* Going back to ancestor directory. */ + /* Clear error so we can continue. */ + result = noErr; + } + } + else + { + /* We hit the bottom of the stack, so we'll let the */ + /* the eofErr drop us out of the loop. */ + result = eofErr; + } + } + } + else + { + /* Special case for index == -1; that means that we're starting */ + /* a new search and so the first item to check is the directory */ + /* passed to us. */ + if ( result == noErr ) + { + /* We found something */ + + CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs); + + /* Now, set the index to 1 and then we're ready to look inside */ + /* the passed directory. */ + index = 1; + } + } + } while ( (!timerTask.stopSearch) && /* timer hasn't fired */ + (result == noErr) && /* no unexpected errors */ + (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */ + + /* Did we drop out of the loop because of timeout or */ + /* ioReqMatchCount was found? */ + if ( result == noErr ) + { + result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize); + if ( result == noErr ) + { + /* Either there was a timeout or ioReqMatchCount was reached. */ + /* Save the dirID and index for the next time we're called. */ + + (*searchStack)[catPosition->stackDepth].dirID = dirID; + (*searchStack)[catPosition->stackDepth].index = index; + (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID); + + /* position catPosition->stackDepth for next saved level */ + + ++(catPosition->stackDepth); + } + } + } + } + else + { + /* searchStack Handle could not be allocated */ + result = memFullErr; + } + } + + if ( pb->ioSearchTime != 0 ) + { + /* Stop Time Manager task here if it was installed */ + RmvTime((QElemPtr)&(timerTask.theTask)); + DisposeTimerUPP(timerTask.theTask.tmAddr); + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock) +{ + OSErr result; + Boolean supportsCatSearch; + GetVolParmsInfoBuffer volParmsInfo; + long infoSize; +#if !__MACOSSEVENORLATER + static Boolean fullExtFSDispatchingtested = false; + static Boolean hasFullExtFSDispatching = false; + long response; +#endif + + result = noErr; + +#if !__MACOSSEVENORLATER + /* See if File Manager will pass CatSearch requests to external file systems */ + /* we'll store the results in a static variable so we don't have to call Gestalt */ + /* everytime we're called. (System 7.0 and later always do this) */ + if ( !fullExtFSDispatchingtested ) + { + fullExtFSDispatchingtested = true; + if ( Gestalt(gestaltFSAttr, &response) == noErr ) + { + hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0); + } + } +#endif + + /* CatSearch is a per volume attribute, so we have to check each time we're */ + /* called to see if it is available on the volume specified. */ + supportsCatSearch = false; +#if !__MACOSSEVENORLATER + if ( hasFullExtFSDispatching ) +#endif + { + infoSize = sizeof(GetVolParmsInfoBuffer); + result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum, + &volParmsInfo, &infoSize); + if ( result == noErr ) + { + supportsCatSearch = hasCatSearch(&volParmsInfo); + } + } + + /* noErr or paramErr is OK here. */ + /* paramErr just means that GetVolParms isn't supported by this volume */ + if ( (result == noErr) || (result == paramErr) ) + { + if ( supportsCatSearch ) + { + /* Volume supports CatSearch so use it. */ + /* CatSearch is faster than an indexed search. */ + result = PBCatSearchSync(paramBlock); + } + else + { + /* Volume doesn't support CatSearch so */ + /* search using IndexedSearch from root directory. */ + result = IndexedSearch(paramBlock, fsRtDirID); + } + } + + return ( result ); +} + +/*****************************************************************************/ + +pascal OSErr NameFileSearch(ConstStr255Param volName, + short vRefNum, + ConstStr255Param fileName, + FSSpecPtr matches, + long reqMatchCount, + long *actMatchCount, + Boolean newSearch, + Boolean partial) +{ + CInfoPBRec searchInfo1, searchInfo2; + HParamBlockRec pb; + OSErr error; + static CatPositionRec catPosition; + static short lastVRefNum = 0; + + /* get the real volume reference number */ + error = DetermineVRefNum(volName, vRefNum, &vRefNum); + if ( error != noErr ) + return ( error ); + + pb.csParam.ioNamePtr = NULL; + pb.csParam.ioVRefNum = vRefNum; + pb.csParam.ioMatchPtr = matches; + pb.csParam.ioReqMatchCount = reqMatchCount; + if ( partial ) /* tell CatSearch what we're looking for: */ + { + pb.csParam.ioSearchBits = fsSBPartialName + fsSBFlAttrib; /* partial name file matches or */ + } + else + { + pb.csParam.ioSearchBits = fsSBFullName + fsSBFlAttrib; /* full name file matches */ + } + pb.csParam.ioSearchInfo1 = &searchInfo1; + pb.csParam.ioSearchInfo2 = &searchInfo2; + pb.csParam.ioSearchTime = 0; + if ( (newSearch) || /* If caller specified new search */ + (lastVRefNum != vRefNum) ) /* or if last search was to another volume, */ + { + catPosition.initialize = 0; /* then search from beginning of catalog */ + } + pb.csParam.ioCatPosition = catPosition; + pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize); + + /* search for fileName */ + searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName; + searchInfo2.hFileInfo.ioNamePtr = NULL; + + /* only match files (not directories) */ + searchInfo1.hFileInfo.ioFlAttrib = 0x00; + searchInfo2.hFileInfo.ioFlAttrib = kioFlAttribDirMask; + + error = PBCatSearchSyncCompat((CSParamPtr)&pb); + + if ( (error == noErr) || /* If no errors or the end of catalog was */ + (error == eofErr) ) /* found, then the call was successful so */ + { + *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */ + } + else + { + *actMatchCount = 0; /* else no matches found */ + } + + if ( (error == noErr) || /* If no errors */ + (error == catChangedErr) ) /* or there was a change in the catalog */ + { + catPosition = pb.csParam.ioCatPosition; + lastVRefNum = vRefNum; + /* we can probably start the next search where we stopped this time */ + } + else + { + catPosition.initialize = 0; + /* start the next search from beginning of catalog */ + } + + if ( pb.csParam.ioOptBuffer != NULL ) + { + DisposePtr(pb.csParam.ioOptBuffer); + } + + return ( error ); +} + +/*****************************************************************************/ + +pascal OSErr CreatorTypeFileSearch(ConstStr255Param volName, + short vRefNum, + OSType creator, + OSType fileType, + FSSpecPtr matches, + long reqMatchCount, + long *actMatchCount, + Boolean newSearch) +{ + CInfoPBRec searchInfo1, searchInfo2; + HParamBlockRec pb; + OSErr error; + static CatPositionRec catPosition; + static short lastVRefNum = 0; + + /* get the real volume reference number */ + error = DetermineVRefNum(volName, vRefNum, &vRefNum); + if ( error != noErr ) + return ( error ); + + pb.csParam.ioNamePtr = NULL; + pb.csParam.ioVRefNum = vRefNum; + pb.csParam.ioMatchPtr = matches; + pb.csParam.ioReqMatchCount = reqMatchCount; + pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo; /* Looking for finder info file matches */ + pb.csParam.ioSearchInfo1 = &searchInfo1; + pb.csParam.ioSearchInfo2 = &searchInfo2; + pb.csParam.ioSearchTime = 0; + if ( (newSearch) || /* If caller specified new search */ + (lastVRefNum != vRefNum) ) /* or if last search was to another volume, */ + { + catPosition.initialize = 0; /* then search from beginning of catalog */ + } + pb.csParam.ioCatPosition = catPosition; + pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize); + + /* no fileName */ + searchInfo1.hFileInfo.ioNamePtr = NULL; + searchInfo2.hFileInfo.ioNamePtr = NULL; + + /* only match files (not directories) */ + searchInfo1.hFileInfo.ioFlAttrib = 0x00; + searchInfo2.hFileInfo.ioFlAttrib = kioFlAttribDirMask; + + /* search for creator; if creator = 0x00000000, ignore creator */ + searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator; + if ( creator == (OSType)0x00000000 ) + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0x00000000; + } + else + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0xffffffff; + } + + /* search for fileType; if fileType = 0x00000000, ignore fileType */ + searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType; + if ( fileType == (OSType)0x00000000 ) + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0x00000000; + } + else + { + searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0xffffffff; + } + + /* zero all other FInfo fields */ + searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0; + searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0; + searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0; + searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0; + + searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0; + searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0; + searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0; + searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0; + + error = PBCatSearchSyncCompat((CSParamPtr)&pb); + + if ( (error == noErr) || /* If no errors or the end of catalog was */ + (error == eofErr) ) /* found, then the call was successful so */ + { + *actMatchCount = pb.csParam.ioActMatchCount; /* return the match count */ + } + else + { + *actMatchCount = 0; /* else no matches found */ + } + + if ( (error == noErr) || /* If no errors */ + (error == catChangedErr) ) /* or there was a change in the catalog */ + { + catPosition = pb.csParam.ioCatPosition; + lastVRefNum = vRefNum; + /* we can probably start the next search where we stopped this time */ + } + else + { + catPosition.initialize = 0; + /* start the next search from beginning of catalog */ + } + + if ( pb.csParam.ioOptBuffer != NULL ) + { + DisposePtr(pb.csParam.ioOptBuffer); + } + + return ( error ); +} + +/*****************************************************************************/ diff --git a/src/osx/carbon/morefile/Search.h b/src/osx/carbon/morefile/Search.h new file mode 100644 index 0000000000..3fee03dea8 --- /dev/null +++ b/src/osx/carbon/morefile/Search.h @@ -0,0 +1,304 @@ +/* + File: Search.h + + Contains: IndexedSearch and the PBCatSearch compatibility function. + + Version: Technology: MoreFiles + Release: 1.5.2 + + Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved. + + Bugs?: For bug reports, consult the following page on + the World Wide Web: + + http://developer.apple.com/bugreporter/ + +*/ + +/* + You may incorporate this sample code into your applications without + restriction, though the sample code has been provided "AS IS" and the + responsibility for its operation is 100% yours. However, what you are + not permitted to do is to redistribute the source as "DSC Sample Code" + after having made changes. If you're going to re-distribute the source, + we require that you make it clear in the source that the code was + descended from Apple Sample Code, but that you've made changes. +*/ + +#ifndef __SEARCH__ +#define __SEARCH__ + +#ifndef __MACTYPES__ +#include +#endif + +#ifndef __FILES__ +#include +#endif + +#include "Optimization.h" + + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +IndexedSearch( + CSParamPtr pb, + long dirID); + + +/* + The IndexedSearch function performs an indexed search in and below the + specified directory using the same parameters (in pb) as is passed to + PBCatSearch. See Inside Macintosh: Files for a description of the + parameter block. + + pb input: A CSParamPtr record specifying the volume to search + and the search criteria. + output: Fields in the parameter block are returned indicating + the number of matches found, the matches, and if the + search ended with noErr, the CatPosition record that + lets you resume a search where the last search left + off. + dirID input: The directory to search. If fsRtDirID is passed, + the entire volume is searched. + + Note: If you use a high-level debugger and use ioSearchTime to limit + the length of time to run the search, you'll want to step over + calls to IndexedSearch because it installs a Time Manager task. + Most high-level debuggers don't deal gracefully with interrupt + driven code. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + + __________ + + See also: PBCatSearch, PBCatSearchSyncCompat +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +PBCatSearchSyncCompat(CSParamPtr paramBlock); + + +/* + The PBCatSearchSyncCompat function uses PBCatSearch (if available) or + IndexedSearch (if PBCatSearch is not available) to search a volume + using a set of search criteria that you specify. It builds a list of all + files or directories that meet your specifications. + + pb input: A CSParamPtr record specifying the volume to search + and the search criteria. + output: Fields in the parameter block are returned indicating + the number of matches found, the matches, and if the + search ended with noErr, the CatPosition record that + lets you resume a search where the last search left + off. + + Note: If you use a high-level debugger and use ioSearchTime to limit + the length of time to run the search, you'll want to step over + calls to PBCatSearchSyncCompat because it calls IndexedSearch + which installs a Time Manager task. Most high-level debuggers + don't deal gracefully with interrupt driven code. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + afpCatalogChanged -5037 Catalog has changed and search cannot + be resumed + + __________ + + See also: PBCatSearch, IndexedSearch +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +NameFileSearch( + ConstStr255Param volName, + short vRefNum, + ConstStr255Param fileName, + FSSpecPtr matches, + long reqMatchCount, + long * actMatchCount, + Boolean newSearch, + Boolean partial); + + +/* + The NameFileSearch function searches for files with a specific file + name on a volume that supports PBCatSearch. + Note: A result of catChangedErr means the catalog has changed between + searches, but the search can be continued with the possiblity that you + may miss some matches or get duplicate matches. For all other results + (except for noErr), the search cannot be continued. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + fileName input: The name of the file to search for. + matches input: Pointer to array of FSSpec where the match list is + returned. + reqMatchCount input: Maximum number of matches to return (the number of + elements in the matches array). + actMatchCount output: The number of matches actually returned. + newSearch input: If true, start a new search. If false and if + vRefNum is the same as the last call to + NameFileSearch, then start searching at the + position where the last search left off. + partial input: If the partial parameter is false, then only files + that exactly match fileName will be found. If the + partial parameter is true, then all file names that + contain fileName will be found. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + afpCatalogChanged -5037 Catalog has changed and search cannot + be resumed + + __________ + + Also see: CreatorTypeFileSearch +*/ + +/*****************************************************************************/ + +EXTERN_API( OSErr ) +CreatorTypeFileSearch( + ConstStr255Param volName, + short vRefNum, + OSType creator, + OSType fileType, + FSSpecPtr matches, + long reqMatchCount, + long * actMatchCount, + Boolean newSearch); + + +/* + The CreatorTypeFileSearch function searches for files with a specific + creator or fileType on a volume that supports PBCatSearch. + Note: A result of catChangedErr means the catalog has changed between + searches, but the search can be continued with the possiblity that you + may miss some matches or get duplicate matches. For all other results + (except for noErr), the search cannot be continued. + + volName input: A pointer to the name of a mounted volume + or nil. + vRefNum input: Volume specification. + creator input: The creator type of the file to search for. + To ignore the creator type, pass 0x00000000 in + this field. + fileType input: The file type of the file to search for. + To ignore the file type, pass 0x00000000 in + this field. + matches input: Pointer to array of FSSpec where the match list is + returned. + reqMatchCount input: Maximum number of matches to return (the number of + elements in the matches array). + actMatchCount output: The number of matches actually returned. + newSearch input: If true, start a new search. If false and if + vRefNum is the same as the last call to + CreatorTypeFileSearch, then start searching at the + position where the last search left off. + + Result Codes + noErr 0 No error + nsvErr -35 Volume not found + ioErr -36 I/O error + eofErr -39 End of catalog found (this is normal!) + paramErr -50 Parameter block has invalid parameters + (see source for VerifyUserPB) or + invalid catPosition record was passed + extFSErr -58 External file system error - no file + system claimed this call. + memFullErr -108 Memory could not be allocated in heap + catChangedErr -1304 Catalog has changed and catalog position + record may be invalid + afpCatalogChanged -5037 Catalog has changed and search cannot + be resumed + + __________ + + Also see: NameFileSearch +*/ + +/*****************************************************************************/ + +#include "OptimizationEnd.h" + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SEARCH__ */ + diff --git a/src/osx/carbon/morefilex/MoreFilesX.c b/src/osx/carbon/morefilex/MoreFilesX.c new file mode 100644 index 0000000000..2b64705351 --- /dev/null +++ b/src/osx/carbon/morefilex/MoreFilesX.c @@ -0,0 +1,2782 @@ +/* + File: MoreFilesX.c + + Contains: A collection of useful high-level File Manager routines + which use the HFS Plus APIs wherever possible. + + Version: MoreFilesX 1.0.1 + + Copyright: © 1992-2002 by Apple Computer, Inc., all rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: For bug reports, consult the following page on + the World Wide Web: + http://developer.apple.com/bugreporter/ + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <4> 8/22/02 JL [3016251] Changed FSMoveRenameObjectUnicode to not use + the Temporary folder because it isn't available on + NFS volumes. + <3> 4/19/02 JL [2853905] Fixed #if test around header includes. + <2> 4/19/02 JL [2850624] Fixed C++ compile errors and Project Builder + warnings. + <2> 4/19/02 JL [2853901] Updated standard disclaimer. + <1> 1/25/02 JL MoreFilesX 1.0 +*/ + +#ifndef __LP64__ + +#if defined(__MACH__) + #include + #include +#else + #include + #include +#endif + +#include "MoreFilesX.h" + +/* Set BuildingMoreFilesXForMacOS9 to 1 if building for Mac OS 9 */ +#ifndef BuildingMoreFilesXForMacOS9 + #define BuildingMoreFilesXForMacOS9 0 +#endif + +/*****************************************************************************/ + +#pragma mark ----- Local type definitions ----- + +struct FSIterateContainerGlobals +{ + IterateContainerFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */ + FSCatalogInfoBitmap whichInfo; /* fields of the CatalogInfo to get */ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ + FSRef ref; /* FSRef */ + FSSpec spec; /* FSSpec */ + FSSpec *specPtr; /* pointer to spec field, or NULL */ + HFSUniStr255 name; /* HFSUniStr255 */ + HFSUniStr255 *namePtr; /* pointer to name field, or NULL */ + void *yourDataPtr; /* a pointer to caller supplied data the filter may need to access */ + ItemCount maxLevels; /* maximum levels to iterate through */ + ItemCount currentLevel; /* the current level FSIterateContainerLevel is on */ + Boolean quitFlag; /* set to true if filter wants to kill interation */ + Boolean containerChanged; /* temporary - set to true if the current container changed during iteration */ + OSErr result; /* result */ + ItemCount actualObjects; /* number of objects returned */ +}; +typedef struct FSIterateContainerGlobals FSIterateContainerGlobals; + +struct FSDeleteContainerGlobals +{ + OSErr result; /* result */ + ItemCount actualObjects; /* number of objects returned */ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ +}; +typedef struct FSDeleteContainerGlobals FSDeleteContainerGlobals; + +/*****************************************************************************/ + +#pragma mark ----- Local prototypes ----- + +static +void +FSDeleteContainerLevel( + const FSRef *container, + FSDeleteContainerGlobals *theGlobals); + +static +void +FSIterateContainerLevel( + FSIterateContainerGlobals *theGlobals); + +static +OSErr +GenerateUniqueHFSUniStr( + long *startSeed, + const FSRef *dir1, + const FSRef *dir2, + HFSUniStr255 *uniqueName); + +/*****************************************************************************/ + +#pragma mark ----- File Access Routines ----- + +/*****************************************************************************/ + +OSErr +FSCopyFork( + SInt16 srcRefNum, + SInt16 dstRefNum, + void *copyBufferPtr, + ByteCount copyBufferSize) +{ + OSErr srcResult; + OSErr dstResult; + OSErr result; + SInt64 forkSize; + ByteCount readActualCount; + + /* check input parameters */ + require_action((NULL != copyBufferPtr) && (0 != copyBufferSize), BadParameter, result = paramErr); + + /* get source fork size */ + result = FSGetForkSize(srcRefNum, &forkSize); + require_noerr(result, SourceFSGetForkSizeFailed); + + /* allocate disk space for destination fork */ + result = FSSetForkSize(dstRefNum, fsFromStart, forkSize); + require_noerr(result, DestinationFSSetForkSizeFailed); + + /* reset source fork's position to 0 */ + result = FSSetForkPosition(srcRefNum, fsFromStart, 0); + require_noerr(result, SourceFSSetForkPositionFailed); + + /* reset destination fork's position to 0 */ + result = FSSetForkPosition(dstRefNum, fsFromStart, 0); + require_noerr(result, DestinationFSSetForkPositionFailed); + + /* If copyBufferSize is greater than 4K bytes, make it a multiple of 4k bytes */ + /* This will make writes on local volumes faster */ + if ( (copyBufferSize >= 0x00001000) && ((copyBufferSize & 0x00000fff) != 0) ) + { + copyBufferSize &= ~(0x00001000 - 1); + } + + /* copy source to destination */ + srcResult = dstResult = noErr; + while ( (noErr == srcResult) && (noErr == dstResult) ) + { + srcResult = FSReadFork(srcRefNum, fsAtMark + noCacheMask, 0, copyBufferSize, copyBufferPtr, &readActualCount); + dstResult = FSWriteFork(dstRefNum, fsAtMark + noCacheMask, 0, readActualCount, copyBufferPtr, NULL); + } + + /* make sure there were no errors at the destination */ + require_noerr_action(dstResult, DestinationFSWriteForkFailed, result = dstResult); + + /* make sure the error at the source was eofErr */ + require_action(eofErr == srcResult, SourceResultNotEofErr, result = srcResult); + + /* everything went as expected */ + result = noErr; + +SourceResultNotEofErr: +DestinationFSWriteForkFailed: +DestinationFSSetForkPositionFailed: +SourceFSSetForkPositionFailed: +DestinationFSSetForkSizeFailed: +SourceFSGetForkSizeFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Volume Access Routines ----- + +/*****************************************************************************/ + +#ifndef __LP64__ + +OSErr +FSGetVolParms( + FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action((NULL != volParmsInfo) && (NULL != actualInfoSize), + BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volParmsInfo; + pb.ioParam.ioReqCount = (SInt32)bufferSize; + result = PBHGetVolParmsSync(&pb); + require_noerr(result, PBHGetVolParmsSync); + + /* return number of bytes the file system returned in volParmsInfo buffer */ + *actualInfoSize = (UInt32)pb.ioParam.ioActCount; + +PBHGetVolParmsSync: +BadParameter: + + return ( result ); +} + +#endif + +/*****************************************************************************/ + +OSErr +FSGetVRefNum( + const FSRef *ref, + FSVolumeRefNum *vRefNum) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != vRefNum, BadParameter, result = paramErr); + + /* get the volume refNum from the FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* return volume refNum from catalogInfo */ + *vRefNum = catalogInfo.volume; + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVInfo( + FSVolumeRefNum volume, + HFSUniStr255 *volumeName, /* can be NULL */ + UInt64 *freeBytes, /* can be NULL */ + UInt64 *totalBytes) /* can be NULL */ +{ + OSErr result; + FSVolumeInfo info; + + /* ask for the volume's sizes only if needed */ + result = FSGetVolumeInfo(volume, 0, NULL, + (((NULL != freeBytes) || (NULL != totalBytes)) ? kFSVolInfoSizes : kFSVolInfoNone), + &info, volumeName, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( NULL != freeBytes ) + { + *freeBytes = info.freeBytes; + } + if ( NULL != totalBytes ) + { + *totalBytes = info.totalBytes; + } + +FSGetVolumeInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolFileSystemID( + FSVolumeRefNum volume, + UInt16 *fileSystemID, /* can be NULL */ + UInt16 *signature) /* can be NULL */ +{ + OSErr result; + FSVolumeInfo info; + + result = FSGetVolumeInfo(volume, 0, NULL, kFSVolInfoFSInfo, &info, NULL, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( NULL != fileSystemID ) + { + *fileSystemID = info.filesystemID; + } + if ( NULL != signature ) + { + *signature = info.signature; + } + +FSGetVolumeInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetMountedVolumes( + FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */ + ItemCount *numVolumes) +{ + OSErr result; + OSErr memResult; + ItemCount volumeIndex; + FSRef ref; + + /* check parameters */ + require_action((NULL != volumeRefsHandle) && (NULL != numVolumes), + BadParameter, result = paramErr); + + /* No volumes yet */ + *numVolumes = 0; + + /* Allocate a handle for the results */ + *volumeRefsHandle = (FSRef **)NewHandle(0); + require_action(NULL != *volumeRefsHandle, NewHandle, result = memFullErr); + + /* Call FSGetVolumeInfo in loop to get all volumes starting with the first */ + volumeIndex = 1; + do + { + result = FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, NULL, &ref); + if ( noErr == result ) + { + /* concatenate the FSRef to the end of the handle */ + PtrAndHand(&ref, (Handle)*volumeRefsHandle, sizeof(FSRef)); + memResult = MemError(); + require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); + + ++(*numVolumes); /* increment the volume count */ + ++volumeIndex; /* and the volumeIndex to get the next volume*/ + } + } while ( noErr == result ); + + /* nsvErr is OK -- it just means there are no more volumes */ + require(nsvErr == result, FSGetVolumeInfo); + + return ( noErr ); + + /**********************/ + +MemoryAllocationFailed: +FSGetVolumeInfo: + + /* dispose of handle if already allocated and clear the outputs */ + if ( NULL != *volumeRefsHandle ) + { + DisposeHandle((Handle)*volumeRefsHandle); + *volumeRefsHandle = NULL; + } + *numVolumes = 0; + +NewHandle: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines ----- + +/*****************************************************************************/ + +OSErr +FSRefMakeFSSpec( + const FSRef *ref, + FSSpec *spec) +{ + OSErr result; + + /* check parameters */ + require_action(NULL != spec, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, spec, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMakeFSRef( + FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref) +{ + OSErr result; + FSRefParam pb; + + /* check parameters */ + require_action(NULL != ref, BadParameter, result = paramErr); + + pb.ioVRefNum = volRefNum; + pb.ioDirID = dirID; + pb.ioNamePtr = (StringPtr)name; + pb.newRef = ref; + result = PBMakeFSRefSync(&pb); + require_noerr(result, PBMakeFSRefSync); + +PBMakeFSRefSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSStatus +FSMakePath( + SInt16 volRefNum, + SInt32 dirID, + ConstStr255Param name, + UInt8 *path, + UInt32 maxPathSize) +{ + OSStatus result; + FSRef ref; + + /* check parameters */ + require_action(NULL != path, BadParameter, result = paramErr); + + /* convert the inputs to an FSRef */ + result = FSMakeFSRef(volRefNum, dirID, name, &ref); + require_noerr(result, FSMakeFSRef); + + /* and then convert the FSRef to a path */ + result = FSRefMakePath(&ref, path, maxPathSize); + require_noerr(result, FSRefMakePath); + +FSRefMakePath: +FSMakeFSRef: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSStatus +FSPathMakeFSSpec( + const UInt8 *path, + FSSpec *spec, + Boolean *isDirectory) /* can be NULL */ +{ + OSStatus result; + FSRef ref; + + /* check parameters */ + require_action(NULL != spec, BadParameter, result = paramErr); + + /* convert the POSIX path to an FSRef */ + result = FSPathMakeRef(path, &ref, isDirectory); + require_noerr(result, FSPathMakeRef); + + /* and then convert the FSRef to an FSSpec */ + result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +FSPathMakeRef: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +UnicodeNameGetHFSName( + UniCharCount nameLength, + const UniChar *name, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str31 hfsName) +{ + OSStatus result; + ByteCount unicodeByteLength; + ByteCount unicodeBytesConverted; + ByteCount actualPascalBytes; + UnicodeMapping uMapping; + UnicodeToTextInfo utInfo; + + /* check parameters */ + require_action(NULL != hfsName, BadParameter, result = paramErr); + + /* make sure output is valid in case we get errors or there's nothing to convert */ + hfsName[0] = 0; + + unicodeByteLength = nameLength * sizeof(UniChar); + if ( 0 == unicodeByteLength ) + { + /* do nothing */ + result = noErr; + } + else + { + /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = (ScriptCode)GetScriptManagerVariable(smSysScript); + region = (RegionCode)GetScriptManagerVariable(smRegionCode); + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, + NULL, &textEncodingHint ); + if ( paramErr == result ) + { + /* ok, ignore the region and try again */ + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, &textEncodingHint ); + } + if ( noErr != result ) + { + /* ok... try something */ + textEncodingHint = kTextEncodingMacRoman; + } + } + + uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + result = CreateUnicodeToTextInfo(&uMapping, &utInfo); + require_noerr(result, CreateUnicodeToTextInfo); + + result = ConvertFromUnicodeToText(utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask, + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars, + &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]); + require_noerr(result, ConvertFromUnicodeToText); + + hfsName[0] = (unsigned char)actualPascalBytes; /* fill in length byte */ + +ConvertFromUnicodeToText: + + /* verify the result in debug builds -- there's really not anything you can do if it fails */ + verify_noerr(DisposeUnicodeToTextInfo(&utInfo)); + } + +CreateUnicodeToTextInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +HFSNameGetUnicodeName( + ConstStr31Param hfsName, + TextEncoding textEncodingHint, + HFSUniStr255 *unicodeName) +{ + ByteCount unicodeByteLength; + OSStatus result; + UnicodeMapping uMapping; + TextToUnicodeInfo tuInfo; + ByteCount pascalCharsRead; + + /* check parameters */ + require_action(NULL != unicodeName, BadParameter, result = paramErr); + + /* make sure output is valid in case we get errors or there's nothing to convert */ + unicodeName->length = 0; + + if ( 0 == StrLength(hfsName) ) + { + result = noErr; + } + else + { + /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = GetScriptManagerVariable(smSysScript); + region = GetScriptManagerVariable(smRegionCode); + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, + NULL, &textEncodingHint); + if ( paramErr == result ) + { + /* ok, ignore the region and try again */ + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, &textEncodingHint); + } + if ( noErr != result ) + { + /* ok... try something */ + textEncodingHint = kTextEncodingMacRoman; + } + } + + uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + result = CreateTextToUnicodeInfo(&uMapping, &tuInfo); + require_noerr(result, CreateTextToUnicodeInfo); + + result = ConvertFromTextToUnicode(tuInfo, hfsName[0], &hfsName[1], + 0, /* no control flag bits */ + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + sizeof(unicodeName->unicode), /* output buffer size in bytes */ + &pascalCharsRead, &unicodeByteLength, unicodeName->unicode); + require_noerr(result, ConvertFromTextToUnicode); + + /* convert from byte count to char count */ + unicodeName->length = unicodeByteLength / sizeof(UniChar); + +ConvertFromTextToUnicode: + + /* verify the result in debug builds -- there's really not anything you can do if it fails */ + verify_noerr(DisposeTextToUnicodeInfo(&tuInfo)); + } + +CreateTextToUnicodeInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- File/Directory Manipulation Routines ----- + +/*****************************************************************************/ + +Boolean FSRefValid(const FSRef *ref) +{ + return ( noErr == FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, NULL, NULL) ); +} + +/*****************************************************************************/ + +OSErr +FSGetParentRef( + const FSRef *ref, + FSRef *parentRef) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != parentRef, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNodeID, &catalogInfo, NULL, NULL, parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* + * Note: FSRefs always point to real file system objects. So, there cannot + * be a FSRef to the parent of volume root directories. Early versions of + * Mac OS X do not handle this case correctly and incorrectly return a + * FSRef for the parent of volume root directories instead of returning an + * invalid FSRef (a cleared FSRef is invalid). The next three lines of code + * ensure that you won't run into this bug. WW9D! + */ + if ( fsRtDirID == catalogInfo.nodeID ) + { + /* clear parentRef and return noErr which is the proper behavior */ + memset(parentRef, 0, sizeof(FSRef)); + } + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetFileDirName( + const FSRef *ref, + HFSUniStr255 *outName) +{ + OSErr result; + + /* check parameters */ + require_action(NULL != outName, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, outName, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetNodeID( + const FSRef *ref, + long *nodeID, /* can be NULL */ + Boolean *isDirectory) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information to get */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != nodeID ) + { + whichInfo |= kFSCatInfoNodeID; + } + if ( NULL != isDirectory ) + { + whichInfo |= kFSCatInfoNodeFlags; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + if ( NULL != nodeID ) + { + *nodeID = catalogInfo.nodeID; + } + if ( NULL != isDirectory ) + { + *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetUserPrivilegesPermissions( + const FSRef *ref, + UInt8 *userPrivileges, /* can be NULL */ + UInt32 permissions[4]) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information to get */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != userPrivileges ) + { + whichInfo |= kFSCatInfoUserPrivs; + } + if ( NULL != permissions ) + { + whichInfo |= kFSCatInfoPermissions; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + if ( NULL != userPrivileges ) + { + *userPrivileges = catalogInfo.userPrivileges; + } + if ( NULL != permissions ) + { + BlockMoveData(&catalogInfo.permissions, permissions, sizeof(UInt32) * 4); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCheckLock( + const FSRef *ref) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSVolumeInfo volumeInfo; + + /* get nodeFlags and vRefNum for container */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoVolume, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* is file locked? */ + if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) + { + result = fLckdErr; /* file is locked */ + } + else + { + /* file isn't locked, but is volume locked? */ + + /* get volume flags */ + result = FSGetVolumeInfo(catalogInfo.volume, 0, NULL, kFSVolInfoFlags, &volumeInfo, NULL, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( 0 != (volumeInfo.flags & kFSVolFlagHardwareLockedMask) ) + { + result = wPrErr; /* volume locked by hardware */ + } + else if ( 0 != (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) ) + { + result = vLckdErr; /* volume locked by software */ + } + } + +FSGetVolumeInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetForkSizes( + const FSRef *ref, + UInt64 *dataLogicalSize, /* can be NULL */ + UInt64 *rsrcLogicalSize) /* can be NULL */ +{ + OSErr result; + FSCatalogInfoBitmap whichInfo; + FSCatalogInfo catalogInfo; + + whichInfo = kFSCatInfoNodeFlags; + if ( NULL != dataLogicalSize ) + { + /* get data fork size */ + whichInfo |= kFSCatInfoDataSizes; + } + if ( NULL != rsrcLogicalSize ) + { + /* get resource fork size */ + whichInfo |= kFSCatInfoRsrcSizes; + } + + /* get nodeFlags and catalog info */ + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* make sure FSRef was to a file */ + require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); + + if ( NULL != dataLogicalSize ) + { + /* return data fork size */ + *dataLogicalSize = catalogInfo.dataLogicalSize; + } + if ( NULL != rsrcLogicalSize ) + { + /* return resource fork size */ + *rsrcLogicalSize = catalogInfo.rsrcLogicalSize; + } + +FSRefNotFile: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetTotalForkSizes( + const FSRef *ref, + UInt64 *totalLogicalSize, /* can be NULL */ + UInt64 *totalPhysicalSize, /* can be NULL */ + ItemCount *forkCount) /* can be NULL */ +{ + OSErr result; + CatPositionRec forkIterator; + SInt64 forkSize; + SInt64 *forkSizePtr; + UInt64 forkPhysicalSize; + UInt64 *forkPhysicalSizePtr; + + /* Determine if forkSize needed */ + if ( NULL != totalLogicalSize) + { + *totalLogicalSize = 0; + forkSizePtr = &forkSize; + } + else + { + forkSizePtr = NULL; + } + + /* Determine if forkPhysicalSize is needed */ + if ( NULL != totalPhysicalSize ) + { + *totalPhysicalSize = 0; + forkPhysicalSizePtr = &forkPhysicalSize; + } + else + { + forkPhysicalSizePtr = NULL; + } + + /* zero fork count if returning it */ + if ( NULL != forkCount ) + { + *forkCount = 0; + } + + /* Iterate through the forks to get the sizes */ + forkIterator.initialize = 0; + do + { + result = FSIterateForks(ref, &forkIterator, NULL, forkSizePtr, forkPhysicalSizePtr); + if ( noErr == result ) + { + if ( NULL != totalLogicalSize ) + { + *totalLogicalSize += forkSize; + } + + if ( NULL != totalPhysicalSize ) + { + *totalPhysicalSize += forkPhysicalSize; + } + + if ( NULL != forkCount ) + { + ++*forkCount; + } + } + } while ( noErr == result ); + + /* any error result other than errFSNoMoreItems is serious */ + require(errFSNoMoreItems == result, FSIterateForks); + + /* Normal exit */ + result = noErr; + +FSIterateForks: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSBumpDate( + const FSRef *ref) +{ + OSStatus result; + FSCatalogInfo catalogInfo; + UTCDateTime oldDateTime; +#if !BuildingMoreFilesXForMacOS9 + FSRef parentRef; + Boolean notifyParent; +#endif + +#if !BuildingMoreFilesXForMacOS9 + /* Get the node flags, the content modification date and time, and the parent ref */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoContentMod, &catalogInfo, NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* Notify the parent if this is a file */ + notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)); +#else + /* Get the content modification date and time */ + result = FSGetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); +#endif + + oldDateTime = catalogInfo.contentModDate; + + /* Get the current date and time */ + result = GetUTCDateTime(&catalogInfo.contentModDate, kUTCDefaultOptions); + require_noerr(result, GetUTCDateTime); + + /* if the old date and time is the the same as the current, bump the seconds by one */ + if ( (catalogInfo.contentModDate.fraction == oldDateTime.fraction) && + (catalogInfo.contentModDate.lowSeconds == oldDateTime.lowSeconds) && + (catalogInfo.contentModDate.highSeconds == oldDateTime.highSeconds) ) + { + ++catalogInfo.contentModDate.lowSeconds; + if ( 0 == catalogInfo.contentModDate.lowSeconds ) + { + ++catalogInfo.contentModDate.highSeconds; + } + } + + /* Bump the content modification date and time */ + result = FSSetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + +#if !BuildingMoreFilesXForMacOS9 + /* + * The problem with FNNotify is that it is not available under Mac OS 9 + * and there's no way to test for that except for looking for the symbol + * or something. So, I'll just conditionalize this for those who care + * to send a notification. + */ + + /* Send a notification for the parent of the file, or for the directory */ + result = FNNotify(notifyParent ? &parentRef : ref, kFNDirectoryModifiedMessage, kNilOptions); + require_noerr(result, FNNotify); +#endif + + /* ignore errors from FSSetCatalogInfo (volume might be write protected) and FNNotify */ +FNNotify: +FSSetCatalogInfo: + + return ( noErr ); + + /**********************/ + +GetUTCDateTime: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetFinderInfo( + const FSRef *ref, + FinderInfo *info, /* can be NULL */ + ExtendedFinderInfo *extendedInfo, /* can be NULL */ + Boolean *isDirectory) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information is really needed */ + whichInfo = kFSCatInfoNone; + + if ( NULL != info ) + { + /* get FinderInfo */ + whichInfo |= kFSCatInfoFinderInfo; + } + + if ( NULL != extendedInfo ) + { + /* get ExtendedFinderInfo */ + whichInfo |= kFSCatInfoFinderXInfo; + } + + if ( NULL != isDirectory ) + { + whichInfo |= kFSCatInfoNodeFlags; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* return FinderInfo if requested */ + if ( NULL != info ) + { + BlockMoveData(catalogInfo.finderInfo, info, sizeof(FinderInfo)); + } + + /* return ExtendedFinderInfo if requested */ + if ( NULL != extendedInfo) + { + BlockMoveData(catalogInfo.extFinderInfo, extendedInfo, sizeof(ExtendedFinderInfo)); + } + + /* set isDirectory Boolean if requested */ + if ( NULL != isDirectory) + { + *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetFinderInfo( + const FSRef *ref, + const FinderInfo *info, + const ExtendedFinderInfo *extendedInfo) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information will be set */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != info ) + { + /* set FinderInfo */ + whichInfo |= kFSCatInfoFinderInfo; + BlockMoveData(info, catalogInfo.finderInfo, sizeof(FinderInfo)); + } + if ( NULL != extendedInfo ) + { + /* set ExtendedFinderInfo */ + whichInfo |= kFSCatInfoFinderXInfo; + BlockMoveData(extendedInfo, catalogInfo.extFinderInfo, sizeof(ExtendedFinderInfo)); + } + + result = FSSetCatalogInfo(ref, whichInfo, &catalogInfo); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSChangeCreatorType( + const FSRef *ref, + OSType fileCreator, + OSType fileType) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSRef parentRef; + + /* get nodeFlags, finder info, and parent FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, &catalogInfo , NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* make sure FSRef was to a file */ + require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); + + /* If fileType not 0x00000000, change fileType */ + if ( fileType != (OSType)0x00000000 ) + { + ((FileInfo *)&catalogInfo.finderInfo)->fileType = fileType; + } + + /* If creator not 0x00000000, change creator */ + if ( fileCreator != (OSType)0x00000000 ) + { + ((FileInfo *)&catalogInfo.finderInfo)->fileCreator = fileCreator; + } + + /* now, save the new information back to disk */ + result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + + /* and attempt to bump the parent directory's mod date to wake up */ + /* the Finder to the change we just made (ignore errors from this) */ + verify_noerr(FSBumpDate(&parentRef)); + +FSSetCatalogInfo: +FSRefNotFile: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSChangeFinderFlags( + const FSRef *ref, + Boolean setBits, + UInt16 flagBits) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSRef parentRef; + + /* get the current finderInfo */ + result = FSGetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* set or clear the appropriate bits in the finderInfo.finderFlags */ + if ( setBits ) + { + /* OR in the bits */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags |= flagBits; + } + else + { + /* AND out the bits */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~flagBits; + } + + /* save the modified finderInfo */ + result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + + /* and attempt to bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made (ignore errors from this) */ + verify_noerr(FSBumpDate(&parentRef)); + +FSSetCatalogInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetInvisible( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kIsInvisible) ); +} + +OSErr +FSClearInvisible( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +OSErr +FSSetNameLocked( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kNameLocked) ); +} + +OSErr +FSClearNameLocked( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kNameLocked) ); +} + +/*****************************************************************************/ + +OSErr +FSSetIsStationery( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kIsStationery) ); +} + +OSErr +FSClearIsStationery( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kIsStationery) ); +} + +/*****************************************************************************/ + +OSErr +FSSetHasCustomIcon( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kHasCustomIcon) ); +} + +OSErr +FSClearHasCustomIcon( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +OSErr +FSClearHasBeenInited( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +OSErr +FSCopyFileMgrAttributes( + const FSRef *sourceRef, + const FSRef *destinationRef, + Boolean copyLockBit) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* get the source information */ + result = FSGetCatalogInfo(sourceRef, kFSCatInfoSettableInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* don't copy the hasBeenInited bit; clear it */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~kHasBeenInited; + + /* should the locked bit be copied? */ + if ( !copyLockBit ) + { + /* no, make sure the locked bit is clear */ + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + } + + /* set the destination information */ + result = FSSetCatalogInfo(destinationRef, kFSCatInfoSettableInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + +FSSetCatalogInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMoveRenameObjectUnicode( + const FSRef *ref, + const FSRef *destDirectory, + UniCharCount nameLength, + const UniChar *name, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* if function fails along the way, newRef is final location of file */ +{ + OSErr result; + FSVolumeRefNum vRefNum; + FSCatalogInfo catalogInfo; + FSRef originalDirectory; + TextEncoding originalTextEncodingHint; + HFSUniStr255 originalName; + HFSUniStr255 uniqueName; /* unique name given to object while moving it to destination */ + long theSeed; /* the seed for generating unique names */ + + /* check parameters */ + require_action(NULL != newRef, BadParameter, result = paramErr); + + /* newRef = input to start with */ + BlockMoveData(ref, newRef, sizeof(FSRef)); + + /* get destDirectory's vRefNum */ + result = FSGetCatalogInfo(destDirectory, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, DestinationBad); + + /* save vRefNum */ + vRefNum = catalogInfo.volume; + + /* get ref's vRefNum, TextEncoding, name and parent directory*/ + result = FSGetCatalogInfo(ref, kFSCatInfoTextEncoding + kFSCatInfoVolume, &catalogInfo, &originalName, NULL, &originalDirectory); + require_noerr(result, SourceBad); + + /* save TextEncoding */ + originalTextEncodingHint = catalogInfo.textEncodingHint; + + /* make sure ref and destDirectory are on same volume */ + require_action(vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* Skip a few steps if we're not renaming */ + if ( NULL != name ) + { + /* generate a name that is unique in both directories */ + theSeed = 0x4a696d4c; /* a fine unlikely filename */ + + result = GenerateUniqueHFSUniStr(&theSeed, &originalDirectory, destDirectory, &uniqueName); + require_noerr(result, GenerateUniqueHFSUniStrFailed); + + /* Rename the object to uniqueName */ + result = FSRenameUnicode(ref, uniqueName.length, uniqueName.unicode, kTextEncodingUnknown, newRef); + require_noerr(result, FSRenameUnicodeBeforeMoveFailed); + + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectAfterRenameFailed); + + /* Rename the object to new name */ + result = FSRenameUnicode(ref, nameLength, name, textEncodingHint, newRef); + require_noerr(result, FSRenameUnicodeAfterMoveFailed); + } + else + { + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectNoRenameFailed); + } + + return ( result ); + + /*************/ + +/* + * failure handling code when renaming + */ + +FSRenameUnicodeAfterMoveFailed: + + /* Error handling: move object back to original location - ignore errors */ + verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef)); + +FSMoveObjectAfterRenameFailed: + + /* Error handling: rename object back to original name - ignore errors */ + verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef)); + +FSRenameUnicodeBeforeMoveFailed: +GenerateUniqueHFSUniStrFailed: + +/* + * failure handling code for renaming or not + */ +FSMoveObjectNoRenameFailed: +NotSameVolume: +SourceBad: +DestinationBad: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The FSDeleteContainerLevel function deletes the contents of a container + directory. All files and subdirectories in the specified container are + deleted. If a locked file or directory is encountered, it is unlocked + and then deleted. If any unexpected errors are encountered, + FSDeleteContainerLevel quits and returns to the caller. + + container --> FSRef to a directory. + theGlobals --> A pointer to a FSDeleteContainerGlobals struct + which contains the variables that do not need to + be allocated each time FSDeleteContainerLevel + recurses. That lets FSDeleteContainerLevel use + less stack space per recursion level. +*/ + +static +void +FSDeleteContainerLevel( + const FSRef *container, + FSDeleteContainerGlobals *theGlobals) +{ + /* level locals */ + FSIterator iterator; + FSRef itemToDelete; + UInt16 nodeFlags; + + /* Open FSIterator for flat access and give delete optimization hint */ + theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator); + require_noerr(theGlobals->result, FSOpenIterator); + + /* delete the contents of the directory */ + do + { + /* get 1 item to delete */ + theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, + NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo, + &itemToDelete, NULL, NULL); + if ( (noErr == theGlobals->result) && (1 == theGlobals->actualObjects) ) + { + /* save node flags in local in case we have to recurse */ + nodeFlags = theGlobals->catalogInfo.nodeFlags; + + /* is it a file or directory? */ + if ( 0 != (nodeFlags & kFSNodeIsDirectoryMask) ) + { + /* it's a directory -- delete its contents before attempting to delete it */ + FSDeleteContainerLevel(&itemToDelete, theGlobals); + } + /* are we still OK to delete? */ + if ( noErr == theGlobals->result ) + { + /* is item locked? */ + if ( 0 != (nodeFlags & kFSNodeLockedMask) ) + { + /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */ + theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo); + } + /* delete the item */ + theGlobals->result = FSDeleteObject(&itemToDelete); + } + } + } while ( noErr == theGlobals->result ); + + /* we found the end of the items normally, so return noErr */ + if ( errFSNoMoreItems == theGlobals->result ) + { + theGlobals->result = noErr; + } + + /* close the FSIterator (closing an open iterator should never fail) */ + verify_noerr(FSCloseIterator(iterator)); + +FSOpenIterator: + + return; +} + +/*****************************************************************************/ + +OSErr +FSDeleteContainerContents( + const FSRef *container) +{ + FSDeleteContainerGlobals theGlobals; + + /* delete container's contents */ + FSDeleteContainerLevel(container, &theGlobals); + + return ( theGlobals.result ); +} + +/*****************************************************************************/ + +OSErr +FSDeleteContainer( + const FSRef *container) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* get nodeFlags for container */ + result = FSGetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* make sure container is a directory */ + require_action(0 != (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), ContainerNotDirectory, result = dirNFErr); + + /* delete container's contents */ + result = FSDeleteContainerContents(container); + require_noerr(result, FSDeleteContainerContents); + + /* is container locked? */ + if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) + { + /* then attempt to unlock container (ignore result since FSDeleteObject will set it correctly) */ + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo); + } + + /* delete the container */ + result = FSDeleteObject(container); + +FSDeleteContainerContents: +ContainerNotDirectory: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The FSIterateContainerLevel function iterates the contents of a container + directory and calls a IterateContainerFilterProc function once for each + file and directory found. + + theGlobals --> A pointer to a FSIterateContainerGlobals struct + which contains the variables needed globally by + all recusion levels of FSIterateContainerLevel. + That makes FSIterateContainer thread safe since + each call to it uses its own global world. + It also contains the variables that do not need + to be allocated each time FSIterateContainerLevel + recurses. That lets FSIterateContainerLevel use + less stack space per recursion level. +*/ + +static +void +FSIterateContainerLevel( + FSIterateContainerGlobals *theGlobals) +{ + FSIterator iterator; + + /* If maxLevels is zero, we aren't checking levels */ + /* If currentLevel < maxLevels, look at this level */ + if ( (theGlobals->maxLevels == 0) || + (theGlobals->currentLevel < theGlobals->maxLevels) ) + { + /* Open FSIterator for flat access to theGlobals->ref */ + theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator); + require_noerr(theGlobals->result, FSOpenIterator); + + ++theGlobals->currentLevel; /* Go to next level */ + + /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ + do + { + theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, + &theGlobals->containerChanged, theGlobals->whichInfo, &theGlobals->catalogInfo, + &theGlobals->ref, theGlobals->specPtr, theGlobals->namePtr); + if ( (noErr == theGlobals->result || errFSNoMoreItems == theGlobals->result) && + (0 != theGlobals->actualObjects) ) + { + /* Call the IterateFilterProc */ + theGlobals->quitFlag = CallIterateContainerFilterProc(theGlobals->iterateFilter, + theGlobals->containerChanged, theGlobals->currentLevel, + &theGlobals->catalogInfo, &theGlobals->ref, + theGlobals->specPtr, theGlobals->namePtr, theGlobals->yourDataPtr); + /* Is it a directory? */ + if ( 0 != (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) ) + { + /* Keep going? */ + if ( !theGlobals->quitFlag ) + { + /* Dive again if the IterateFilterProc didn't say "quit" */ + FSIterateContainerLevel(theGlobals); + } + } + } + /* time to fall back a level? */ + } while ( (noErr == theGlobals->result) && (!theGlobals->quitFlag) ); + + /* errFSNoMoreItems is OK - it only means we hit the end of this level */ + /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */ + if ( (errFSNoMoreItems == theGlobals->result) || + (afpAccessDenied == theGlobals->result) ) + { + theGlobals->result = noErr; + } + + --theGlobals->currentLevel; /* Return to previous level as we leave */ + + /* Close the FSIterator (closing an open iterator should never fail) */ + verify_noerr(FSCloseIterator(iterator)); + } + +FSOpenIterator: + + return; +} + +/*****************************************************************************/ + +OSErr +FSIterateContainer( + const FSRef *container, + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + Boolean wantFSSpec, + Boolean wantName, + IterateContainerFilterProcPtr iterateFilter, + void *yourDataPtr) +{ + OSErr result; + FSIterateContainerGlobals theGlobals; + + /* make sure there is an iterateFilter */ + require_action(iterateFilter != NULL, NoIterateFilter, result = paramErr); + + /* + * set up the globals we need to access from the recursive routine + */ + theGlobals.iterateFilter = iterateFilter; + /* we need the node flags no matter what was requested so we can detect files vs. directories */ + theGlobals.whichInfo = whichInfo | kFSCatInfoNodeFlags; + /* start with input container -- the first OpenIterator will ensure it is a directory */ + theGlobals.ref = *container; + if ( wantFSSpec ) + { + theGlobals.specPtr = &theGlobals.spec; + } + else + { + theGlobals.specPtr = NULL; + } + if ( wantName ) + { + theGlobals.namePtr = &theGlobals.name; + } + else + { + theGlobals.namePtr = NULL; + } + theGlobals.yourDataPtr = yourDataPtr; + theGlobals.maxLevels = maxLevels; + theGlobals.currentLevel = 0; + theGlobals.quitFlag = false; + theGlobals.containerChanged = false; + theGlobals.result = noErr; + theGlobals.actualObjects = 0; + + /* here we go into recursion land... */ + FSIterateContainerLevel(&theGlobals); + result = theGlobals.result; + require_noerr(result, FSIterateContainerLevel); + +FSIterateContainerLevel: +NoIterateFilter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetDirectoryItems( + const FSRef *container, + FSRef ***refsHandle, /* pointer to handle of FSRefs */ + ItemCount *numRefs, + Boolean *containerChanged) +{ + /* Grab items 10 at a time. */ + enum { kMaxItemsPerBulkCall = 10 }; + + OSErr result; + OSErr memResult; + FSIterator iterator; + FSRef refs[kMaxItemsPerBulkCall]; + ItemCount actualObjects; + Boolean changed; + + /* check parameters */ + require_action((NULL != refsHandle) && (NULL != numRefs) && (NULL != containerChanged), + BadParameter, result = paramErr); + + *numRefs = 0; + *containerChanged = false; + *refsHandle = (FSRef **)NewHandle(0); + require_action(NULL != *refsHandle, NewHandle, result = memFullErr); + + /* open an FSIterator */ + result = FSOpenIterator(container, kFSIterateFlat, &iterator); + require_noerr(result, FSOpenIterator); + + /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ + do + { + result = FSGetCatalogInfoBulk(iterator, kMaxItemsPerBulkCall, &actualObjects, + &changed, kFSCatInfoNone, NULL, refs, NULL, NULL); + + /* if the container changed, set containerChanged for output, but keep going */ + if ( changed ) + { + *containerChanged = changed; + } + + /* any result other than noErr and errFSNoMoreItems is serious */ + require((noErr == result) || (errFSNoMoreItems == result), FSGetCatalogInfoBulk); + + /* add objects to output array and count */ + if ( 0 != actualObjects ) + { + /* concatenate the FSRefs to the end of the handle */ + PtrAndHand(refs, (Handle)*refsHandle, actualObjects * sizeof(FSRef)); + memResult = MemError(); + require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); + + *numRefs += actualObjects; + } + } while ( noErr == result ); + + verify_noerr(FSCloseIterator(iterator)); /* closing an open iterator should never fail, but... */ + + return ( noErr ); + + /**********************/ + +MemoryAllocationFailed: +FSGetCatalogInfoBulk: + + /* close the iterator */ + verify_noerr(FSCloseIterator(iterator)); + +FSOpenIterator: + /* dispose of handle if already allocated and clear the outputs */ + if ( NULL != *refsHandle ) + { + DisposeHandle((Handle)*refsHandle); + *refsHandle = NULL; + } + *numRefs = 0; + +NewHandle: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The GenerateUniqueName function generates a HFSUniStr255 name that is + unique in both dir1 and dir2. + + startSeed --> A pointer to a long which is used to generate the + unique name. + <-- It is modified on output to a value which should + be used to generate the next unique name. + dir1 --> The first directory. + dir2 --> The second directory. + uniqueName <-- A pointer to a HFSUniStr255 where the unique name + is to be returned. +*/ + +static +OSErr +GenerateUniqueHFSUniStr( + long *startSeed, + const FSRef *dir1, + const FSRef *dir2, + HFSUniStr255 *uniqueName) +{ + OSErr result; + long i; + FSRefParam pb; + FSRef newRef; + unsigned char hexStr[17] = "0123456789ABCDEF"; + + /* set up the parameter block */ + pb.name = uniqueName->unicode; + pb.nameLength = 8; /* always 8 characters */ + pb.textEncodingHint = kTextEncodingUnknown; + pb.newRef = &newRef; + + /* loop until we get fnfErr with a filename in both directories */ + result = noErr; + while ( fnfErr != result ) + { + /* convert startSeed to 8 character Unicode string */ + uniqueName->length = 8; + for ( i = 0; i < 8; ++i ) + { + uniqueName->unicode[i] = hexStr[((*startSeed >> ((7-i)*4)) & 0xf)]; + } + + /* try in dir1 */ + pb.ref = dir1; + result = PBMakeFSRefUnicodeSync(&pb); + if ( fnfErr == result ) + { + /* try in dir2 */ + pb.ref = dir2; + result = PBMakeFSRefUnicodeSync(&pb); + if ( fnfErr != result ) + { + /* exit if anything other than noErr or fnfErr */ + require_noerr(result, Dir2PBMakeFSRefUnicodeSyncFailed); + } + } + else + { + /* exit if anything other than noErr or fnfErr */ + require_noerr(result, Dir1PBMakeFSRefUnicodeSyncFailed); + } + + /* increment seed for next pass through loop, */ + /* or for next call to GenerateUniqueHFSUniStr */ + ++(*startSeed); + } + + /* we have a unique file name which doesn't exist in dir1 or dir2 */ + result = noErr; + +Dir2PBMakeFSRefUnicodeSyncFailed: +Dir1PBMakeFSRefUnicodeSyncFailed: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSExchangeObjectsCompat( + const FSRef *sourceRef, + const FSRef *destRef, + FSRef *newSourceRef, + FSRef *newDestRef) +{ + enum + { + /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */ + kGetCatInformationMask = (kFSCatInfoSettableInfo | + kFSCatInfoVolume | + kFSCatInfoParentDirID) & + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod), + /* set everything possible except for mod dates */ + kSetCatinformationMask = kFSCatInfoSettableInfo & + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod) + }; + + OSErr result; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + FSCatalogInfo sourceCatalogInfo; /* source file's catalog information */ + FSCatalogInfo destCatalogInfo; /* destination file's catalog information */ + HFSUniStr255 sourceName; /* source file's Unicode name */ + HFSUniStr255 destName; /* destination file's Unicode name */ + FSRef sourceCurrentRef; /* FSRef to current location of source file throughout this function */ + FSRef destCurrentRef; /* FSRef to current location of destination file throughout this function */ + FSRef sourceParentRef; /* FSRef to parent directory of source file */ + FSRef destParentRef; /* FSRef to parent directory of destination file */ + HFSUniStr255 sourceUniqueName; /* unique name given to source file while exchanging it with destination */ + HFSUniStr255 destUniqueName; /* unique name given to destination file while exchanging it with source */ + long theSeed; /* the seed for generating unique names */ + Boolean sameParentDirs; /* true if source and destinatin parent directory is the same */ + + /* check parameters */ + require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr); + + /* output refs and current refs = input refs to start with */ + BlockMoveData(sourceRef, newSourceRef, sizeof(FSRef)); + BlockMoveData(sourceRef, &sourceCurrentRef, sizeof(FSRef)); + + BlockMoveData(destRef, newDestRef, sizeof(FSRef)); + BlockMoveData(destRef, &destCurrentRef, sizeof(FSRef)); + + /* get source volume's vRefNum */ + result = FSGetCatalogInfo(&sourceCurrentRef, kFSCatInfoVolume, &sourceCatalogInfo, NULL, NULL, NULL); + require_noerr(result, DetermineSourceVRefNumFailed); + + /* see if that volume supports FSExchangeObjects */ + result = FSGetVolParms(sourceCatalogInfo.volume, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + if ( (noErr == result) && VolSupportsFSExchangeObjects(&volParmsInfo) ) + { + /* yes - use FSExchangeObjects */ + result = FSExchangeObjects(sourceRef, destRef); + } + else + { + /* no - emulate FSExchangeObjects */ + + /* Note: The compatibility case won't work for files with *Btree control blocks. */ + /* Right now the only *Btree files are created by the system. */ + + /* get all catalog information and Unicode names for each file */ + result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef); + require_noerr(result, SourceFSGetCatalogInfoFailed); + + result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef); + require_noerr(result, DestFSGetCatalogInfoFailed); + + /* make sure source and destination are on same volume */ + require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* make sure both files are *really* files */ + require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) && + (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr); + + /* generate 2 names that are unique in both directories */ + theSeed = 0x4a696d4c; /* a fine unlikely filename */ + + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName); + require_noerr(result, GenerateUniqueHFSUniStr1Failed); + + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName); + require_noerr(result, GenerateUniqueHFSUniStr2Failed); + + /* rename sourceCurrentRef to sourceUniqueName */ + result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef); + require_noerr(result, FSRenameUnicode1Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* rename destCurrentRef to destUniqueName */ + result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef); + require_noerr(result, FSRenameUnicode2Failed); + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + + /* are the source and destination parent directories the same? */ + sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID ); + if ( !sameParentDirs ) + { + /* move source file to dest parent directory */ + result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef); + require_noerr(result, FSMoveObject1Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* move dest file to source parent directory */ + result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef); + require_noerr(result, FSMoveObject2Failed); + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + } + + /* At this point, the files are in their new locations (if they were moved). */ + /* The source file is named sourceUniqueName and is in the directory referred to */ + /* by destParentRef. The destination file is named destUniqueName and is in the */ + /* directory referred to by sourceParentRef. */ + + /* give source file the dest file's catalog information except for mod dates */ + result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo); + require_noerr(result, FSSetCatalogInfo1Failed); + + /* give dest file the source file's catalog information except for mod dates */ + result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo); + require_noerr(result, FSSetCatalogInfo2Failed); + + /* rename source file with dest file's name */ + result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef); + require_noerr(result, FSRenameUnicode3Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* rename dest file with source file's name */ + result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef); + require_noerr(result, FSRenameUnicode4Failed); + + /* we're done with no errors, so swap newSourceRef and newDestRef */ + BlockMoveData(newDestRef, newSourceRef, sizeof(FSRef)); + BlockMoveData(&sourceCurrentRef, newDestRef, sizeof(FSRef)); + } + + return ( result ); + + /**********************/ + +/* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */ +/* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */ +/* state and location they ended up in so that both files can be found by the calling code. */ + +FSRenameUnicode4Failed: + + /* attempt to rename source file to sourceUniqueName */ + if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) ) + { + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + } + +FSRenameUnicode3Failed: + + /* attempt to restore dest file's catalog information */ + verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo)); + +FSSetCatalogInfo2Failed: + + /* attempt to restore source file's catalog information */ + verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo)); + +FSSetCatalogInfo1Failed: + + if ( !sameParentDirs ) + { + /* attempt to move dest file back to dest directory */ + if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) ) + { + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + } + } + +FSMoveObject2Failed: + + if ( !sameParentDirs ) + { + /* attempt to move source file back to source directory */ + if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) ) + { + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + } + } + +FSMoveObject1Failed: + + /* attempt to rename dest file to original name */ + verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef)); + +FSRenameUnicode2Failed: + + /* attempt to rename source file to original name */ + verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef)); + +FSRenameUnicode1Failed: +GenerateUniqueHFSUniStr2Failed: +GenerateUniqueHFSUniStr1Failed: +NotAFile: +NotSameVolume: +DestFSGetCatalogInfoFailed: +SourceFSGetCatalogInfoFailed: +DetermineSourceVRefNumFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Shared Environment Routines ----- + +/*****************************************************************************/ + +#ifndef __LP64__ + +OSErr +FSLockRangeMoreFilesX( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart) +{ + OSErr result; + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + result = PBLockRangeSync(&pb); + require_noerr(result, PBLockRangeSync); + +PBLockRangeSync: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSUnlockRangeMoreFilesX( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart) +{ + OSErr result; + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + result = PBUnlockRangeSync(&pb); + require_noerr(result, PBUnlockRangeSync); + +PBUnlockRangeSync: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetDirAccess( + const FSRef *ref, + SInt32 *ownerID, /* can be NULL */ + SInt32 *groupID, /* can be NULL */ + SInt32 *accessRights) /* can be NULL */ +{ + OSErr result; + FSSpec spec; + HParamBlockRec pb; + + /* get FSSpec from FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* get directory access info for FSSpec */ + pb.accessParam.ioNamePtr = (StringPtr)spec.name; + pb.accessParam.ioVRefNum = spec.vRefNum; + pb.fileParam.ioDirID = spec.parID; + result = PBHGetDirAccessSync(&pb); + require_noerr(result, PBHGetDirAccessSync); + + /* return the IDs and access rights */ + if ( NULL != ownerID ) + { + *ownerID = pb.accessParam.ioACOwnerID; + } + if ( NULL != groupID ) + { + *groupID = pb.accessParam.ioACGroupID; + } + if ( NULL != accessRights ) + { + *accessRights = pb.accessParam.ioACAccess; + } + +PBHGetDirAccessSync: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetDirAccess( + const FSRef *ref, + SInt32 ownerID, + SInt32 groupID, + SInt32 accessRights) +{ + OSErr result; + FSSpec spec; + HParamBlockRec pb; + + enum + { + /* Just the bits that can be set */ + kSetDirAccessSettableMask = (kioACAccessBlankAccessMask + + kioACAccessEveryoneWriteMask + kioACAccessEveryoneReadMask + kioACAccessEveryoneSearchMask + + kioACAccessGroupWriteMask + kioACAccessGroupReadMask + kioACAccessGroupSearchMask + + kioACAccessOwnerWriteMask + kioACAccessOwnerReadMask + kioACAccessOwnerSearchMask) + }; + + /* get FSSpec from FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* set directory access info for FSSpec */ + pb.accessParam.ioNamePtr = (StringPtr)spec.name; + pb.accessParam.ioVRefNum = spec.vRefNum; + pb.fileParam.ioDirID = spec.parID; + pb.accessParam.ioACOwnerID = ownerID; + pb.accessParam.ioACGroupID = groupID; + pb.accessParam.ioACAccess = accessRights & kSetDirAccessSettableMask; + result = PBHSetDirAccessSync(&pb); + require_noerr(result, PBHSetDirAccessSync); + +PBHSetDirAccessSync: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolMountInfoSize( + FSVolumeRefNum volRefNum, + SInt16 *size) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != size, BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)size; + result = PBGetVolMountInfoSize(&pb); + require_noerr(result, PBGetVolMountInfoSize); + +PBGetVolMountInfoSize: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolMountInfo( + FSVolumeRefNum volRefNum, + void *volMountInfo) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != volMountInfo, BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + result = PBGetVolMountInfo(&pb); + require_noerr(result, PBGetVolMountInfo); + +PBGetVolMountInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSVolumeMountX( + const void *volMountInfo, + FSVolumeRefNum *volRefNum) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != volRefNum, BadParameter, result = paramErr); + + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + result = PBVolumeMount(&pb); + require_noerr(result, PBVolumeMount); + + /* return the volume reference number */ + *volRefNum = pb.ioParam.ioVRefNum; + +PBVolumeMount: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMapID( + FSVolumeRefNum volRefNum, + SInt32 ugID, + SInt16 objType, + Str31 name) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action(NULL != name, BadParameter, result = paramErr); + + pb.objParam.ioNamePtr = NULL; + pb.objParam.ioVRefNum = volRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = name; + pb.objParam.ioObjID = ugID; + result = PBHMapIDSync(&pb); + require_noerr(result, PBHMapIDSync); + +PBHMapIDSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMapName( + FSVolumeRefNum volRefNum, + ConstStr255Param name, + SInt16 objType, + SInt32 *ugID) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action(NULL != ugID, BadParameter, result = paramErr); + + pb.objParam.ioNamePtr = NULL; + pb.objParam.ioVRefNum = volRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = (StringPtr)name; + result = PBHMapNameSync(&pb); + require_noerr(result, PBHMapNameSync); + + /* return the user or group ID */ + *ugID = pb.objParam.ioObjID; + +PBHMapNameSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCopyFile( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *copyName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* can be NULL */ +{ + OSErr result; + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + HParamBlockRec pb; + Str31 hfsName; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + + /* get source FSSpec from source FSRef */ + result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + require_noerr(result, FSGetCatalogInfo_srcFileRef); + + /* Make sure the volume supports CopyFile */ + result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + require_action((noErr == result) && VolHasCopyFile(&volParmsInfo), + NoCopyFileSupport, result = paramErr); + + /* get destination volume reference number and destination directory ID from destination FSRef */ + result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); + + /* tell the server to copy the object */ + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioDstVRefNum = catalogInfo.volume; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if ( NULL != copyName ) + { + result = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName); + require_noerr(result, UnicodeNameGetHFSName); + + pb.copyParam.ioCopyName = hfsName; + } + else + { + pb.copyParam.ioCopyName = NULL; + } + result = PBHCopyFileSync(&pb); + require_noerr(result, PBHCopyFileSync); + + if ( NULL != newRef ) + { + verify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, + pb.copyParam.ioCopyName, newRef)); + } + +PBHCopyFileSync: +UnicodeNameGetHFSName: +FSGetCatalogInfo_dstDirectoryRef: +NoCopyFileSupport: +FSGetCatalogInfo_srcFileRef: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMoveRename( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *moveName, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* can be NULL */ +{ + OSErr result; + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + HParamBlockRec pb; + Str31 hfsName; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + + /* get source FSSpec from source FSRef */ + result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + require_noerr(result, FSGetCatalogInfo_srcFileRef); + + /* Make sure the volume supports MoveRename */ + result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + require_action((noErr == result) && VolHasMoveRename(&volParmsInfo), + NoMoveRenameSupport, result = paramErr); + + /* get destination volume reference number and destination directory ID from destination FSRef */ + result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); + + /* make sure the source and destination are on the same volume */ + require_action(srcFileSpec.vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* tell the server to move and rename the object */ + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if ( NULL != moveName ) + { + result = UnicodeNameGetHFSName(nameLength, moveName, textEncodingHint, false, hfsName); + require_noerr(result, UnicodeNameGetHFSName); + + pb.copyParam.ioCopyName = hfsName; + } + else + { + pb.copyParam.ioCopyName = NULL; + } + result = PBHMoveRenameSync(&pb); + require_noerr(result, PBHMoveRenameSync); + + if ( NULL != newRef ) + { + verify_noerr(FSMakeFSRef(pb.copyParam.ioVRefNum, pb.copyParam.ioNewDirID, + pb.copyParam.ioCopyName, newRef)); + } + +PBHMoveRenameSync: +UnicodeNameGetHFSName: +NotSameVolume: +FSGetCatalogInfo_dstDirectoryRef: +NoMoveRenameSupport: +FSGetCatalogInfo_srcFileRef: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- File ID Routines ----- + +/*****************************************************************************/ + +OSErr +FSResolveFileIDRef( + FSVolumeRefNum volRefNum, + SInt32 fileID, + FSRef *ref) +{ + OSErr result; + FIDParam pb; + Str255 tempStr; + + /* check parameters */ + require_action(NULL != ref, BadParameter, result = paramErr); + + /* resolve the file ID reference */ + tempStr[0] = 0; + pb.ioNamePtr = tempStr; + pb.ioVRefNum = volRefNum; + pb.ioFileID = fileID; + result = PBResolveFileIDRefSync((HParmBlkPtr)&pb); + require_noerr(result, PBResolveFileIDRefSync); + + /* and then make an FSRef to the file */ + result = FSMakeFSRef(volRefNum, pb.ioSrcDirID, tempStr, ref); + require_noerr(result, FSMakeFSRef); + +FSMakeFSRef: +PBResolveFileIDRefSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCreateFileIDRef( + const FSRef *ref, + SInt32 *fileID) +{ + OSErr result; + FSSpec spec; + FIDParam pb; + + /* check parameters */ + require_action(NULL != fileID, BadParameter, result = paramErr); + + /* Get an FSSpec from the FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Create (or get) the file ID reference using the FSSpec */ + pb.ioNamePtr = (StringPtr)spec.name; + pb.ioVRefNum = spec.vRefNum; + pb.ioSrcDirID = spec.parID; + result = PBCreateFileIDRefSync((HParmBlkPtr)&pb); + require((noErr == result) || (fidExists == result) || (afpIDExists == result), + PBCreateFileIDRefSync); + + /* return the file ID reference */ + *fileID = pb.ioFileID; + +PBCreateFileIDRefSync: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +#endif + +/*****************************************************************************/ + +#pragma mark ----- Utility Routines ----- + +/*****************************************************************************/ + +Ptr +GetTempBuffer( + ByteCount buffReqSize, + ByteCount *buffActSize) +{ + enum + { + kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */ + }; + + Ptr tempPtr; + + /* check parameters */ + require_action(NULL != buffActSize, BadParameter, tempPtr = NULL); + + /* Make request a multiple of 4K bytes */ + buffReqSize = buffReqSize & 0xfffff000; + + if ( buffReqSize < 0x00001000 ) + { + /* Request was smaller than 4K bytes - make it 4K */ + buffReqSize = 0x00001000; + } + + /* Attempt to allocate the memory */ + tempPtr = NewPtr(buffReqSize); + + /* If request failed, go to backup plan */ + if ( (tempPtr == NULL) && (buffReqSize > 0x00001000) ) + { + /* + ** Try to get largest 4K byte block available + ** leaving some slop for the toolbox if possible + */ + long freeMemory = (FreeMem() - kSlopMemory) & 0xfffff000; + + buffReqSize = MaxBlock() & 0xfffff000; + + if ( (long)buffReqSize > freeMemory ) + { + buffReqSize = freeMemory; + } + + if ( buffReqSize == 0 ) + { + buffReqSize = 0x00001000; + } + + tempPtr = NewPtr(buffReqSize); + } + + /* Return bytes allocated */ + if ( tempPtr != NULL ) + { + *buffActSize = buffReqSize; + } + else + { + *buffActSize = 0; + } + +BadParameter: + + return ( tempPtr ); +} + +/*****************************************************************************/ + +OSErr +FileRefNumGetFSRef( + short refNum, + FSRef *ref) +{ + return ( FSGetForkCBInfo(refNum, 0, NULL, NULL, NULL, ref, NULL) ); +} + +/*****************************************************************************/ + +OSErr +FSSetDefault( + const FSRef *newDefault, + FSRef *oldDefault) +{ + OSErr result; + FSVolumeRefNum vRefNum; + long dirID; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action((NULL != newDefault) && (NULL != oldDefault), BadParameter, result = paramErr); + + /* Get nodeFlags, vRefNum and dirID (nodeID) of newDefault */ + result = FSGetCatalogInfo(newDefault, + kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Make sure newDefault is a directory */ + require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), NewDefaultNotDirectory, + result = dirNFErr); + + /* Get the current working directory. */ + result = HGetVol(NULL, &vRefNum, &dirID); + require_noerr(result, HGetVol); + + /* Return the oldDefault FSRef */ + result = FSMakeFSRef(vRefNum, dirID, NULL, oldDefault); + require_noerr(result, FSMakeFSRef); + + /* Set the new current working directory */ + result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); + require_noerr(result, HSetVol); + +HSetVol: +FSMakeFSRef: +HGetVol: +NewDefaultNotDirectory: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSRestoreDefault( + const FSRef *oldDefault) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != oldDefault, BadParameter, result = paramErr); + + /* Get nodeFlags, vRefNum and dirID (nodeID) of oldDefault */ + result = FSGetCatalogInfo(oldDefault, + kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Make sure oldDefault is a directory */ + require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), OldDefaultNotDirectory, + result = dirNFErr); + + /* Set the current working directory to oldDefault */ + result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); + require_noerr(result, HSetVol); + +HSetVol: +OldDefaultNotDirectory: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#endif diff --git a/src/osx/carbon/morefilex/MoreFilesX.cpp b/src/osx/carbon/morefilex/MoreFilesX.cpp new file mode 100644 index 0000000000..fdd7a8f634 --- /dev/null +++ b/src/osx/carbon/morefilex/MoreFilesX.cpp @@ -0,0 +1,2772 @@ +/* + File: MoreFilesX.c + + Contains: A collection of useful high-level File Manager routines + which use the HFS Plus APIs wherever possible. + + Version: MoreFilesX 1.0.1 + + Copyright: © 1992-2002 by Apple Computer, Inc., all rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: For bug reports, consult the following page on + the World Wide Web: + http://developer.apple.com/bugreporter/ + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <4> 8/22/02 JL [3016251] Changed FSMoveRenameObjectUnicode to not use + the Temporary folder because it isn't available on + NFS volumes. + <3> 4/19/02 JL [2853905] Fixed #if test around header includes. + <2> 4/19/02 JL [2850624] Fixed C++ compile errors and Project Builder + warnings. + <2> 4/19/02 JL [2853901] Updated standard disclaimer. + <1> 1/25/02 JL MoreFilesX 1.0 +*/ + +#if defined(__MACH__) + #include + #include + #define BuildingMoreFilesXForMacOS9 0 +#else + #include + #include + #define BuildingMoreFilesXForMacOS9 1 +#endif + +#include "MoreFilesX.h" + +/* Set BuildingMoreFilesXForMacOS9 to 1 if building for Mac OS 9 */ +#ifndef BuildingMoreFilesXForMacOS9 + #define BuildingMoreFilesXForMacOS9 0 +#endif + +/*****************************************************************************/ + +#pragma mark ----- Local type definitions ----- + +struct FSIterateContainerGlobals +{ + IterateContainerFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */ + FSCatalogInfoBitmap whichInfo; /* fields of the CatalogInfo to get */ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ + FSRef ref; /* FSRef */ + FSSpec spec; /* FSSpec */ + FSSpec *specPtr; /* pointer to spec field, or NULL */ + HFSUniStr255 name; /* HFSUniStr255 */ + HFSUniStr255 *namePtr; /* pointer to name field, or NULL */ + void *yourDataPtr; /* a pointer to caller supplied data the filter may need to access */ + ItemCount maxLevels; /* maximum levels to iterate through */ + ItemCount currentLevel; /* the current level FSIterateContainerLevel is on */ + Boolean quitFlag; /* set to true if filter wants to kill interation */ + Boolean containerChanged; /* temporary - set to true if the current container changed during iteration */ + OSErr result; /* result */ + ItemCount actualObjects; /* number of objects returned */ +}; +typedef struct FSIterateContainerGlobals FSIterateContainerGlobals; + +struct FSDeleteContainerGlobals +{ + OSErr result; /* result */ + ItemCount actualObjects; /* number of objects returned */ + FSCatalogInfo catalogInfo; /* FSCatalogInfo */ +}; +typedef struct FSDeleteContainerGlobals FSDeleteContainerGlobals; + +/*****************************************************************************/ + +#pragma mark ----- Local prototypes ----- + +static +void +FSDeleteContainerLevel( + const FSRef *container, + FSDeleteContainerGlobals *theGlobals); + +static +void +FSIterateContainerLevel( + FSIterateContainerGlobals *theGlobals); + +static +OSErr +GenerateUniqueHFSUniStr( + long *startSeed, + const FSRef *dir1, + const FSRef *dir2, + HFSUniStr255 *uniqueName); + +/*****************************************************************************/ + +#pragma mark ----- File Access Routines ----- + +/*****************************************************************************/ + +OSErr +FSCopyFork( + SInt16 srcRefNum, + SInt16 dstRefNum, + void *copyBufferPtr, + ByteCount copyBufferSize) +{ + OSErr srcResult; + OSErr dstResult; + OSErr result; + SInt64 forkSize; + ByteCount readActualCount; + + /* check input parameters */ + require_action((NULL != copyBufferPtr) && (0 != copyBufferSize), BadParameter, result = paramErr); + + /* get source fork size */ + result = FSGetForkSize(srcRefNum, &forkSize); + require_noerr(result, SourceFSGetForkSizeFailed); + + /* allocate disk space for destination fork */ + result = FSSetForkSize(dstRefNum, fsFromStart, forkSize); + require_noerr(result, DestinationFSSetForkSizeFailed); + + /* reset source fork's position to 0 */ + result = FSSetForkPosition(srcRefNum, fsFromStart, 0); + require_noerr(result, SourceFSSetForkPositionFailed); + + /* reset destination fork's position to 0 */ + result = FSSetForkPosition(dstRefNum, fsFromStart, 0); + require_noerr(result, DestinationFSSetForkPositionFailed); + + /* If copyBufferSize is greater than 4K bytes, make it a multiple of 4k bytes */ + /* This will make writes on local volumes faster */ + if ( (copyBufferSize >= 0x00001000) && ((copyBufferSize & 0x00000fff) != 0) ) + { + copyBufferSize &= ~(0x00001000 - 1); + } + + /* copy source to destination */ + srcResult = dstResult = noErr; + while ( (noErr == srcResult) && (noErr == dstResult) ) + { + srcResult = FSReadFork(srcRefNum, fsAtMark + noCacheMask, 0, copyBufferSize, copyBufferPtr, &readActualCount); + dstResult = FSWriteFork(dstRefNum, fsAtMark + noCacheMask, 0, readActualCount, copyBufferPtr, NULL); + } + + /* make sure there were no errors at the destination */ + require_noerr_action(dstResult, DestinationFSWriteForkFailed, result = dstResult); + + /* make sure the error at the source was eofErr */ + require_action(eofErr == srcResult, SourceResultNotEofErr, result = srcResult); + + /* everything went as expected */ + result = noErr; + +SourceResultNotEofErr: +DestinationFSWriteForkFailed: +DestinationFSSetForkPositionFailed: +SourceFSSetForkPositionFailed: +DestinationFSSetForkSizeFailed: +SourceFSGetForkSizeFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Volume Access Routines ----- + +/*****************************************************************************/ + +OSErr +FSGetVolParms( + FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action((NULL != volParmsInfo) && (NULL != actualInfoSize), + BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volParmsInfo; + pb.ioParam.ioReqCount = (SInt32)bufferSize; + result = PBHGetVolParmsSync(&pb); + require_noerr(result, PBHGetVolParmsSync); + + /* return number of bytes the file system returned in volParmsInfo buffer */ + *actualInfoSize = (UInt32)pb.ioParam.ioActCount; + +PBHGetVolParmsSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVRefNum( + const FSRef *ref, + FSVolumeRefNum *vRefNum) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != vRefNum, BadParameter, result = paramErr); + + /* get the volume refNum from the FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* return volume refNum from catalogInfo */ + *vRefNum = catalogInfo.volume; + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVInfo( + FSVolumeRefNum volume, + HFSUniStr255 *volumeName, /* can be NULL */ + UInt64 *freeBytes, /* can be NULL */ + UInt64 *totalBytes) /* can be NULL */ +{ + OSErr result; + FSVolumeInfo info; + + /* ask for the volume's sizes only if needed */ + result = FSGetVolumeInfo(volume, 0, NULL, + (((NULL != freeBytes) || (NULL != totalBytes)) ? kFSVolInfoSizes : kFSVolInfoNone), + &info, volumeName, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( NULL != freeBytes ) + { + *freeBytes = info.freeBytes; + } + if ( NULL != totalBytes ) + { + *totalBytes = info.totalBytes; + } + +FSGetVolumeInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolFileSystemID( + FSVolumeRefNum volume, + UInt16 *fileSystemID, /* can be NULL */ + UInt16 *signature) /* can be NULL */ +{ + OSErr result; + FSVolumeInfo info; + + result = FSGetVolumeInfo(volume, 0, NULL, kFSVolInfoFSInfo, &info, NULL, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( NULL != fileSystemID ) + { + *fileSystemID = info.filesystemID; + } + if ( NULL != signature ) + { + *signature = info.signature; + } + +FSGetVolumeInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetMountedVolumes( + FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */ + ItemCount *numVolumes) +{ + OSErr result; + OSErr memResult; + ItemCount volumeIndex; + FSRef ref; + + /* check parameters */ + require_action((NULL != volumeRefsHandle) && (NULL != numVolumes), + BadParameter, result = paramErr); + + /* No volumes yet */ + *numVolumes = 0; + + /* Allocate a handle for the results */ + *volumeRefsHandle = (FSRef **)NewHandle(0); + require_action(NULL != *volumeRefsHandle, NewHandle, result = memFullErr); + + /* Call FSGetVolumeInfo in loop to get all volumes starting with the first */ + volumeIndex = 1; + do + { + result = FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, NULL, &ref); + if ( noErr == result ) + { + /* concatenate the FSRef to the end of the handle */ + PtrAndHand(&ref, (Handle)*volumeRefsHandle, sizeof(FSRef)); + memResult = MemError(); + require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); + + ++(*numVolumes); /* increment the volume count */ + ++volumeIndex; /* and the volumeIndex to get the next volume*/ + } + } while ( noErr == result ); + + /* nsvErr is OK -- it just means there are no more volumes */ + require(nsvErr == result, FSGetVolumeInfo); + + return ( noErr ); + + /**********************/ + +MemoryAllocationFailed: +FSGetVolumeInfo: + + /* dispose of handle if already allocated and clear the outputs */ + if ( NULL != *volumeRefsHandle ) + { + DisposeHandle((Handle)*volumeRefsHandle); + *volumeRefsHandle = NULL; + } + *numVolumes = 0; + +NewHandle: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines ----- + +/*****************************************************************************/ + +OSErr +FSRefMakeFSSpec( + const FSRef *ref, + FSSpec *spec) +{ + OSErr result; + + /* check parameters */ + require_action(NULL != spec, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, spec, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMakeFSRef( + FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref) +{ + OSErr result; + FSRefParam pb; + + /* check parameters */ + require_action(NULL != ref, BadParameter, result = paramErr); + + pb.ioVRefNum = volRefNum; + pb.ioDirID = dirID; + pb.ioNamePtr = (StringPtr)name; + pb.newRef = ref; + result = PBMakeFSRefSync(&pb); + require_noerr(result, PBMakeFSRefSync); + +PBMakeFSRefSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSStatus +FSMakePath( + SInt16 volRefNum, + SInt32 dirID, + ConstStr255Param name, + UInt8 *path, + UInt32 maxPathSize) +{ + OSStatus result; + FSRef ref; + + /* check parameters */ + require_action(NULL != path, BadParameter, result = paramErr); + + /* convert the inputs to an FSRef */ + result = FSMakeFSRef(volRefNum, dirID, name, &ref); + require_noerr(result, FSMakeFSRef); + + /* and then convert the FSRef to a path */ + result = FSRefMakePath(&ref, path, maxPathSize); + require_noerr(result, FSRefMakePath); + +FSRefMakePath: +FSMakeFSRef: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSStatus +FSPathMakeFSSpec( + const UInt8 *path, + FSSpec *spec, + Boolean *isDirectory) /* can be NULL */ +{ + OSStatus result; + FSRef ref; + + /* check parameters */ + require_action(NULL != spec, BadParameter, result = paramErr); + + /* convert the POSIX path to an FSRef */ + result = FSPathMakeRef(path, &ref, isDirectory); + require_noerr(result, FSPathMakeRef); + + /* and then convert the FSRef to an FSSpec */ + result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +FSPathMakeRef: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +UnicodeNameGetHFSName( + UniCharCount nameLength, + const UniChar *name, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str31 hfsName) +{ + OSStatus result; + ByteCount unicodeByteLength; + ByteCount unicodeBytesConverted; + ByteCount actualPascalBytes; + UnicodeMapping uMapping; + UnicodeToTextInfo utInfo; + + /* check parameters */ + require_action(NULL != hfsName, BadParameter, result = paramErr); + + /* make sure output is valid in case we get errors or there's nothing to convert */ + hfsName[0] = 0; + + unicodeByteLength = nameLength * sizeof(UniChar); + if ( 0 == unicodeByteLength ) + { + /* do nothing */ + result = noErr; + } + else + { + /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = (ScriptCode)GetScriptManagerVariable(smSysScript); + region = (RegionCode)GetScriptManagerVariable(smRegionCode); + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, + NULL, &textEncodingHint ); + if ( paramErr == result ) + { + /* ok, ignore the region and try again */ + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, &textEncodingHint ); + } + if ( noErr != result ) + { + /* ok... try something */ + textEncodingHint = kTextEncodingMacRoman; + } + } + + uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + result = CreateUnicodeToTextInfo(&uMapping, &utInfo); + require_noerr(result, CreateUnicodeToTextInfo); + + result = ConvertFromUnicodeToText(utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask, + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars, + &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]); + require_noerr(result, ConvertFromUnicodeToText); + + hfsName[0] = (unsigned char)actualPascalBytes; /* fill in length byte */ + +ConvertFromUnicodeToText: + + /* verify the result in debug builds -- there's really not anything you can do if it fails */ + verify_noerr(DisposeUnicodeToTextInfo(&utInfo)); + } + +CreateUnicodeToTextInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +HFSNameGetUnicodeName( + ConstStr31Param hfsName, + TextEncoding textEncodingHint, + HFSUniStr255 *unicodeName) +{ + ByteCount unicodeByteLength; + OSStatus result; + UnicodeMapping uMapping; + TextToUnicodeInfo tuInfo; + ByteCount pascalCharsRead; + + /* check parameters */ + require_action(NULL != unicodeName, BadParameter, result = paramErr); + + /* make sure output is valid in case we get errors or there's nothing to convert */ + unicodeName->length = 0; + + if ( 0 == StrLength(hfsName) ) + { + result = noErr; + } + else + { + /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ + if ( kTextEncodingUnknown == textEncodingHint ) + { + ScriptCode script; + RegionCode region; + + script = GetScriptManagerVariable(smSysScript); + region = GetScriptManagerVariable(smRegionCode); + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, + NULL, &textEncodingHint); + if ( paramErr == result ) + { + /* ok, ignore the region and try again */ + result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, + kTextRegionDontCare, NULL, &textEncodingHint); + } + if ( noErr != result ) + { + /* ok... try something */ + textEncodingHint = kTextEncodingMacRoman; + } + } + + uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, + kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); + uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); + uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; + + result = CreateTextToUnicodeInfo(&uMapping, &tuInfo); + require_noerr(result, CreateTextToUnicodeInfo); + + result = ConvertFromTextToUnicode(tuInfo, hfsName[0], &hfsName[1], + 0, /* no control flag bits */ + 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ + sizeof(unicodeName->unicode), /* output buffer size in bytes */ + &pascalCharsRead, &unicodeByteLength, unicodeName->unicode); + require_noerr(result, ConvertFromTextToUnicode); + + /* convert from byte count to char count */ + unicodeName->length = unicodeByteLength / sizeof(UniChar); + +ConvertFromTextToUnicode: + + /* verify the result in debug builds -- there's really not anything you can do if it fails */ + verify_noerr(DisposeTextToUnicodeInfo(&tuInfo)); + } + +CreateTextToUnicodeInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- File/Directory Manipulation Routines ----- + +/*****************************************************************************/ + +Boolean FSRefValid(const FSRef *ref) +{ + return ( noErr == FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, NULL, NULL) ); +} + +/*****************************************************************************/ + +OSErr +FSGetParentRef( + const FSRef *ref, + FSRef *parentRef) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != parentRef, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNodeID, &catalogInfo, NULL, NULL, parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* + * Note: FSRefs always point to real file system objects. So, there cannot + * be a FSRef to the parent of volume root directories. Early versions of + * Mac OS X do not handle this case correctly and incorrectly return a + * FSRef for the parent of volume root directories instead of returning an + * invalid FSRef (a cleared FSRef is invalid). The next three lines of code + * ensure that you won't run into this bug. WW9D! + */ + if ( fsRtDirID == catalogInfo.nodeID ) + { + /* clear parentRef and return noErr which is the proper behavior */ + memset(parentRef, 0, sizeof(FSRef)); + } + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetFileDirName( + const FSRef *ref, + HFSUniStr255 *outName) +{ + OSErr result; + + /* check parameters */ + require_action(NULL != outName, BadParameter, result = paramErr); + + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, outName, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetNodeID( + const FSRef *ref, + long *nodeID, /* can be NULL */ + Boolean *isDirectory) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information to get */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != nodeID ) + { + whichInfo |= kFSCatInfoNodeID; + } + if ( NULL != isDirectory ) + { + whichInfo |= kFSCatInfoNodeFlags; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + if ( NULL != nodeID ) + { + *nodeID = catalogInfo.nodeID; + } + if ( NULL != isDirectory ) + { + *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetUserPrivilegesPermissions( + const FSRef *ref, + UInt8 *userPrivileges, /* can be NULL */ + UInt32 permissions[4]) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information to get */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != userPrivileges ) + { + whichInfo |= kFSCatInfoUserPrivs; + } + if ( NULL != permissions ) + { + whichInfo |= kFSCatInfoPermissions; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + if ( NULL != userPrivileges ) + { + *userPrivileges = catalogInfo.userPrivileges; + } + if ( NULL != permissions ) + { + BlockMoveData(&catalogInfo.permissions, permissions, sizeof(UInt32) * 4); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCheckLock( + const FSRef *ref) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSVolumeInfo volumeInfo; + + /* get nodeFlags and vRefNum for container */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoVolume, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* is file locked? */ + if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) + { + result = fLckdErr; /* file is locked */ + } + else + { + /* file isn't locked, but is volume locked? */ + + /* get volume flags */ + result = FSGetVolumeInfo(catalogInfo.volume, 0, NULL, kFSVolInfoFlags, &volumeInfo, NULL, NULL); + require_noerr(result, FSGetVolumeInfo); + + if ( 0 != (volumeInfo.flags & kFSVolFlagHardwareLockedMask) ) + { + result = wPrErr; /* volume locked by hardware */ + } + else if ( 0 != (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) ) + { + result = vLckdErr; /* volume locked by software */ + } + } + +FSGetVolumeInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetForkSizes( + const FSRef *ref, + UInt64 *dataLogicalSize, /* can be NULL */ + UInt64 *rsrcLogicalSize) /* can be NULL */ +{ + OSErr result; + FSCatalogInfoBitmap whichInfo; + FSCatalogInfo catalogInfo; + + whichInfo = kFSCatInfoNodeFlags; + if ( NULL != dataLogicalSize ) + { + /* get data fork size */ + whichInfo |= kFSCatInfoDataSizes; + } + if ( NULL != rsrcLogicalSize ) + { + /* get resource fork size */ + whichInfo |= kFSCatInfoRsrcSizes; + } + + /* get nodeFlags and catalog info */ + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* make sure FSRef was to a file */ + require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); + + if ( NULL != dataLogicalSize ) + { + /* return data fork size */ + *dataLogicalSize = catalogInfo.dataLogicalSize; + } + if ( NULL != rsrcLogicalSize ) + { + /* return resource fork size */ + *rsrcLogicalSize = catalogInfo.rsrcLogicalSize; + } + +FSRefNotFile: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetTotalForkSizes( + const FSRef *ref, + UInt64 *totalLogicalSize, /* can be NULL */ + UInt64 *totalPhysicalSize, /* can be NULL */ + ItemCount *forkCount) /* can be NULL */ +{ + OSErr result; + CatPositionRec forkIterator; + SInt64 forkSize; + SInt64 *forkSizePtr; + UInt64 forkPhysicalSize; + UInt64 *forkPhysicalSizePtr; + + /* Determine if forkSize needed */ + if ( NULL != totalLogicalSize) + { + *totalLogicalSize = 0; + forkSizePtr = &forkSize; + } + else + { + forkSizePtr = NULL; + } + + /* Determine if forkPhysicalSize is needed */ + if ( NULL != totalPhysicalSize ) + { + *totalPhysicalSize = 0; + forkPhysicalSizePtr = &forkPhysicalSize; + } + else + { + forkPhysicalSizePtr = NULL; + } + + /* zero fork count if returning it */ + if ( NULL != forkCount ) + { + *forkCount = 0; + } + + /* Iterate through the forks to get the sizes */ + forkIterator.initialize = 0; + do + { + result = FSIterateForks(ref, &forkIterator, NULL, forkSizePtr, forkPhysicalSizePtr); + if ( noErr == result ) + { + if ( NULL != totalLogicalSize ) + { + *totalLogicalSize += forkSize; + } + + if ( NULL != totalPhysicalSize ) + { + *totalPhysicalSize += forkPhysicalSize; + } + + if ( NULL != forkCount ) + { + ++*forkCount; + } + } + } while ( noErr == result ); + + /* any error result other than errFSNoMoreItems is serious */ + require(errFSNoMoreItems == result, FSIterateForks); + + /* Normal exit */ + result = noErr; + +FSIterateForks: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSBumpDate( + const FSRef *ref) +{ + OSStatus result; + FSCatalogInfo catalogInfo; + UTCDateTime oldDateTime; +#if !BuildingMoreFilesXForMacOS9 + FSRef parentRef; + Boolean notifyParent; +#endif + +#if !BuildingMoreFilesXForMacOS9 + /* Get the node flags, the content modification date and time, and the parent ref */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoContentMod, &catalogInfo, NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* Notify the parent if this is a file */ + notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)); +#else + /* Get the content modification date and time */ + result = FSGetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); +#endif + + oldDateTime = catalogInfo.contentModDate; + + /* Get the current date and time */ + result = GetUTCDateTime(&catalogInfo.contentModDate, kUTCDefaultOptions); + require_noerr(result, GetUTCDateTime); + + /* if the old date and time is the the same as the current, bump the seconds by one */ + if ( (catalogInfo.contentModDate.fraction == oldDateTime.fraction) && + (catalogInfo.contentModDate.lowSeconds == oldDateTime.lowSeconds) && + (catalogInfo.contentModDate.highSeconds == oldDateTime.highSeconds) ) + { + ++catalogInfo.contentModDate.lowSeconds; + if ( 0 == catalogInfo.contentModDate.lowSeconds ) + { + ++catalogInfo.contentModDate.highSeconds; + } + } + + /* Bump the content modification date and time */ + result = FSSetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + +#if !BuildingMoreFilesXForMacOS9 + /* + * The problem with FNNotify is that it is not available under Mac OS 9 + * and there's no way to test for that except for looking for the symbol + * or something. So, I'll just conditionalize this for those who care + * to send a notification. + */ + + /* Send a notification for the parent of the file, or for the directory */ + result = FNNotify(notifyParent ? &parentRef : ref, kFNDirectoryModifiedMessage, kNilOptions); + require_noerr(result, FNNotify); +#endif + + /* ignore errors from FSSetCatalogInfo (volume might be write protected) and FNNotify */ +FNNotify: +FSSetCatalogInfo: + + return ( noErr ); + + /**********************/ + +GetUTCDateTime: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetFinderInfo( + const FSRef *ref, + FinderInfo *info, /* can be NULL */ + ExtendedFinderInfo *extendedInfo, /* can be NULL */ + Boolean *isDirectory) /* can be NULL */ +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information is really needed */ + whichInfo = kFSCatInfoNone; + + if ( NULL != info ) + { + /* get FinderInfo */ + whichInfo |= kFSCatInfoFinderInfo; + } + + if ( NULL != extendedInfo ) + { + /* get ExtendedFinderInfo */ + whichInfo |= kFSCatInfoFinderXInfo; + } + + if ( NULL != isDirectory ) + { + whichInfo |= kFSCatInfoNodeFlags; + } + + result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* return FinderInfo if requested */ + if ( NULL != info ) + { + BlockMoveData(catalogInfo.finderInfo, info, sizeof(FinderInfo)); + } + + /* return ExtendedFinderInfo if requested */ + if ( NULL != extendedInfo) + { + BlockMoveData(catalogInfo.extFinderInfo, extendedInfo, sizeof(ExtendedFinderInfo)); + } + + /* set isDirectory Boolean if requested */ + if ( NULL != isDirectory) + { + *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); + } + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetFinderInfo( + const FSRef *ref, + const FinderInfo *info, + const ExtendedFinderInfo *extendedInfo) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSCatalogInfoBitmap whichInfo; + + /* determine what catalog information will be set */ + whichInfo = kFSCatInfoNone; /* start with none */ + if ( NULL != info ) + { + /* set FinderInfo */ + whichInfo |= kFSCatInfoFinderInfo; + BlockMoveData(info, catalogInfo.finderInfo, sizeof(FinderInfo)); + } + if ( NULL != extendedInfo ) + { + /* set ExtendedFinderInfo */ + whichInfo |= kFSCatInfoFinderXInfo; + BlockMoveData(extendedInfo, catalogInfo.extFinderInfo, sizeof(ExtendedFinderInfo)); + } + + result = FSSetCatalogInfo(ref, whichInfo, &catalogInfo); + require_noerr(result, FSGetCatalogInfo); + +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSChangeCreatorType( + const FSRef *ref, + OSType fileCreator, + OSType fileType) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSRef parentRef; + + /* get nodeFlags, finder info, and parent FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, &catalogInfo , NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* make sure FSRef was to a file */ + require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); + + /* If fileType not 0x00000000, change fileType */ + if ( fileType != (OSType)0x00000000 ) + { + ((FileInfo *)&catalogInfo.finderInfo)->fileType = fileType; + } + + /* If creator not 0x00000000, change creator */ + if ( fileCreator != (OSType)0x00000000 ) + { + ((FileInfo *)&catalogInfo.finderInfo)->fileCreator = fileCreator; + } + + /* now, save the new information back to disk */ + result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + + /* and attempt to bump the parent directory's mod date to wake up */ + /* the Finder to the change we just made (ignore errors from this) */ + verify_noerr(FSBumpDate(&parentRef)); + +FSSetCatalogInfo: +FSRefNotFile: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSChangeFinderFlags( + const FSRef *ref, + Boolean setBits, + UInt16 flagBits) +{ + OSErr result; + FSCatalogInfo catalogInfo; + FSRef parentRef; + + /* get the current finderInfo */ + result = FSGetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, &parentRef); + require_noerr(result, FSGetCatalogInfo); + + /* set or clear the appropriate bits in the finderInfo.finderFlags */ + if ( setBits ) + { + /* OR in the bits */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags |= flagBits; + } + else + { + /* AND out the bits */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~flagBits; + } + + /* save the modified finderInfo */ + result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + + /* and attempt to bump the parent directory's mod date to wake up the Finder */ + /* to the change we just made (ignore errors from this) */ + verify_noerr(FSBumpDate(&parentRef)); + +FSSetCatalogInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetInvisible( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kIsInvisible) ); +} + +OSErr +FSClearInvisible( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kIsInvisible) ); +} + +/*****************************************************************************/ + +OSErr +FSSetNameLocked( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kNameLocked) ); +} + +OSErr +FSClearNameLocked( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kNameLocked) ); +} + +/*****************************************************************************/ + +OSErr +FSSetIsStationery( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kIsStationery) ); +} + +OSErr +FSClearIsStationery( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kIsStationery) ); +} + +/*****************************************************************************/ + +OSErr +FSSetHasCustomIcon( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, true, kHasCustomIcon) ); +} + +OSErr +FSClearHasCustomIcon( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kHasCustomIcon) ); +} + +/*****************************************************************************/ + +OSErr +FSClearHasBeenInited( + const FSRef *ref) +{ + return ( FSChangeFinderFlags(ref, false, kHasBeenInited) ); +} + +/*****************************************************************************/ + +OSErr +FSCopyFileMgrAttributes( + const FSRef *sourceRef, + const FSRef *destinationRef, + Boolean copyLockBit) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* get the source information */ + result = FSGetCatalogInfo(sourceRef, kFSCatInfoSettableInfo, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* don't copy the hasBeenInited bit; clear it */ + ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~kHasBeenInited; + + /* should the locked bit be copied? */ + if ( !copyLockBit ) + { + /* no, make sure the locked bit is clear */ + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + } + + /* set the destination information */ + result = FSSetCatalogInfo(destinationRef, kFSCatInfoSettableInfo, &catalogInfo); + require_noerr(result, FSSetCatalogInfo); + +FSSetCatalogInfo: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMoveRenameObjectUnicode( + const FSRef *ref, + const FSRef *destDirectory, + UniCharCount nameLength, + const UniChar *name, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* if function fails along the way, newRef is final location of file */ +{ + OSErr result; + FSVolumeRefNum vRefNum; + FSCatalogInfo catalogInfo; + FSRef originalDirectory; + TextEncoding originalTextEncodingHint; + HFSUniStr255 originalName; + HFSUniStr255 uniqueName; /* unique name given to object while moving it to destination */ + long theSeed; /* the seed for generating unique names */ + + /* check parameters */ + require_action(NULL != newRef, BadParameter, result = paramErr); + + /* newRef = input to start with */ + BlockMoveData(ref, newRef, sizeof(FSRef)); + + /* get destDirectory's vRefNum */ + result = FSGetCatalogInfo(destDirectory, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); + require_noerr(result, DestinationBad); + + /* save vRefNum */ + vRefNum = catalogInfo.volume; + + /* get ref's vRefNum, TextEncoding, name and parent directory*/ + result = FSGetCatalogInfo(ref, kFSCatInfoTextEncoding + kFSCatInfoVolume, &catalogInfo, &originalName, NULL, &originalDirectory); + require_noerr(result, SourceBad); + + /* save TextEncoding */ + originalTextEncodingHint = catalogInfo.textEncodingHint; + + /* make sure ref and destDirectory are on same volume */ + require_action(vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* Skip a few steps if we're not renaming */ + if ( NULL != name ) + { + /* generate a name that is unique in both directories */ + theSeed = 0x4a696d4c; /* a fine unlikely filename */ + + result = GenerateUniqueHFSUniStr(&theSeed, &originalDirectory, destDirectory, &uniqueName); + require_noerr(result, GenerateUniqueHFSUniStrFailed); + + /* Rename the object to uniqueName */ + result = FSRenameUnicode(ref, uniqueName.length, uniqueName.unicode, kTextEncodingUnknown, newRef); + require_noerr(result, FSRenameUnicodeBeforeMoveFailed); + + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectAfterRenameFailed); + + /* Rename the object to new name */ + result = FSRenameUnicode(ref, nameLength, name, textEncodingHint, newRef); + require_noerr(result, FSRenameUnicodeAfterMoveFailed); + } + else + { + /* Move object to its new home */ + result = FSMoveObject(newRef, destDirectory, newRef); + require_noerr(result, FSMoveObjectNoRenameFailed); + } + + return ( result ); + + /*************/ + +/* + * failure handling code when renaming + */ + +FSRenameUnicodeAfterMoveFailed: + + /* Error handling: move object back to original location - ignore errors */ + verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef)); + +FSMoveObjectAfterRenameFailed: + + /* Error handling: rename object back to original name - ignore errors */ + verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef)); + +FSRenameUnicodeBeforeMoveFailed: +GenerateUniqueHFSUniStrFailed: + +/* + * failure handling code for renaming or not + */ +FSMoveObjectNoRenameFailed: +NotSameVolume: +SourceBad: +DestinationBad: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The FSDeleteContainerLevel function deletes the contents of a container + directory. All files and subdirectories in the specified container are + deleted. If a locked file or directory is encountered, it is unlocked + and then deleted. If any unexpected errors are encountered, + FSDeleteContainerLevel quits and returns to the caller. + + container --> FSRef to a directory. + theGlobals --> A pointer to a FSDeleteContainerGlobals struct + which contains the variables that do not need to + be allocated each time FSDeleteContainerLevel + recurses. That lets FSDeleteContainerLevel use + less stack space per recursion level. +*/ + +static +void +FSDeleteContainerLevel( + const FSRef *container, + FSDeleteContainerGlobals *theGlobals) +{ + /* level locals */ + FSIterator iterator; + FSRef itemToDelete; + UInt16 nodeFlags; + + /* Open FSIterator for flat access and give delete optimization hint */ + theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator); + require_noerr(theGlobals->result, FSOpenIterator); + + /* delete the contents of the directory */ + do + { + /* get 1 item to delete */ + theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, + NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo, + &itemToDelete, NULL, NULL); + if ( (noErr == theGlobals->result) && (1 == theGlobals->actualObjects) ) + { + /* save node flags in local in case we have to recurse */ + nodeFlags = theGlobals->catalogInfo.nodeFlags; + + /* is it a file or directory? */ + if ( 0 != (nodeFlags & kFSNodeIsDirectoryMask) ) + { + /* it's a directory -- delete its contents before attempting to delete it */ + FSDeleteContainerLevel(&itemToDelete, theGlobals); + } + /* are we still OK to delete? */ + if ( noErr == theGlobals->result ) + { + /* is item locked? */ + if ( 0 != (nodeFlags & kFSNodeLockedMask) ) + { + /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */ + theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo); + } + /* delete the item */ + theGlobals->result = FSDeleteObject(&itemToDelete); + } + } + } while ( noErr == theGlobals->result ); + + /* we found the end of the items normally, so return noErr */ + if ( errFSNoMoreItems == theGlobals->result ) + { + theGlobals->result = noErr; + } + + /* close the FSIterator (closing an open iterator should never fail) */ + verify_noerr(FSCloseIterator(iterator)); + +FSOpenIterator: + + return; +} + +/*****************************************************************************/ + +OSErr +FSDeleteContainerContents( + const FSRef *container) +{ + FSDeleteContainerGlobals theGlobals; + + /* delete container's contents */ + FSDeleteContainerLevel(container, &theGlobals); + + return ( theGlobals.result ); +} + +/*****************************************************************************/ + +OSErr +FSDeleteContainer( + const FSRef *container) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* get nodeFlags for container */ + result = FSGetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL); + require_noerr(result, FSGetCatalogInfo); + + /* make sure container is a directory */ + require_action(0 != (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), ContainerNotDirectory, result = dirNFErr); + + /* delete container's contents */ + result = FSDeleteContainerContents(container); + require_noerr(result, FSDeleteContainerContents); + + /* is container locked? */ + if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) + { + /* then attempt to unlock container (ignore result since FSDeleteObject will set it correctly) */ + catalogInfo.nodeFlags &= ~kFSNodeLockedMask; + (void) FSSetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo); + } + + /* delete the container */ + result = FSDeleteObject(container); + +FSDeleteContainerContents: +ContainerNotDirectory: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The FSIterateContainerLevel function iterates the contents of a container + directory and calls a IterateContainerFilterProc function once for each + file and directory found. + + theGlobals --> A pointer to a FSIterateContainerGlobals struct + which contains the variables needed globally by + all recusion levels of FSIterateContainerLevel. + That makes FSIterateContainer thread safe since + each call to it uses its own global world. + It also contains the variables that do not need + to be allocated each time FSIterateContainerLevel + recurses. That lets FSIterateContainerLevel use + less stack space per recursion level. +*/ + +static +void +FSIterateContainerLevel( + FSIterateContainerGlobals *theGlobals) +{ + FSIterator iterator; + + /* If maxLevels is zero, we aren't checking levels */ + /* If currentLevel < maxLevels, look at this level */ + if ( (theGlobals->maxLevels == 0) || + (theGlobals->currentLevel < theGlobals->maxLevels) ) + { + /* Open FSIterator for flat access to theGlobals->ref */ + theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator); + require_noerr(theGlobals->result, FSOpenIterator); + + ++theGlobals->currentLevel; /* Go to next level */ + + /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ + do + { + theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, + &theGlobals->containerChanged, theGlobals->whichInfo, &theGlobals->catalogInfo, + &theGlobals->ref, theGlobals->specPtr, theGlobals->namePtr); + if ( (noErr == theGlobals->result || errFSNoMoreItems == theGlobals->result) && + (0 != theGlobals->actualObjects) ) + { + /* Call the IterateFilterProc */ + theGlobals->quitFlag = CallIterateContainerFilterProc(theGlobals->iterateFilter, + theGlobals->containerChanged, theGlobals->currentLevel, + &theGlobals->catalogInfo, &theGlobals->ref, + theGlobals->specPtr, theGlobals->namePtr, theGlobals->yourDataPtr); + /* Is it a directory? */ + if ( 0 != (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) ) + { + /* Keep going? */ + if ( !theGlobals->quitFlag ) + { + /* Dive again if the IterateFilterProc didn't say "quit" */ + FSIterateContainerLevel(theGlobals); + } + } + } + /* time to fall back a level? */ + } while ( (noErr == theGlobals->result) && (!theGlobals->quitFlag) ); + + /* errFSNoMoreItems is OK - it only means we hit the end of this level */ + /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */ + if ( (errFSNoMoreItems == theGlobals->result) || + (afpAccessDenied == theGlobals->result) ) + { + theGlobals->result = noErr; + } + + --theGlobals->currentLevel; /* Return to previous level as we leave */ + + /* Close the FSIterator (closing an open iterator should never fail) */ + verify_noerr(FSCloseIterator(iterator)); + } + +FSOpenIterator: + + return; +} + +/*****************************************************************************/ + +OSErr +FSIterateContainer( + const FSRef *container, + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + Boolean wantFSSpec, + Boolean wantName, + IterateContainerFilterProcPtr iterateFilter, + void *yourDataPtr) +{ + OSErr result; + FSIterateContainerGlobals theGlobals; + + /* make sure there is an iterateFilter */ + require_action(iterateFilter != NULL, NoIterateFilter, result = paramErr); + + /* + * set up the globals we need to access from the recursive routine + */ + theGlobals.iterateFilter = iterateFilter; + /* we need the node flags no matter what was requested so we can detect files vs. directories */ + theGlobals.whichInfo = whichInfo | kFSCatInfoNodeFlags; + /* start with input container -- the first OpenIterator will ensure it is a directory */ + theGlobals.ref = *container; + if ( wantFSSpec ) + { + theGlobals.specPtr = &theGlobals.spec; + } + else + { + theGlobals.specPtr = NULL; + } + if ( wantName ) + { + theGlobals.namePtr = &theGlobals.name; + } + else + { + theGlobals.namePtr = NULL; + } + theGlobals.yourDataPtr = yourDataPtr; + theGlobals.maxLevels = maxLevels; + theGlobals.currentLevel = 0; + theGlobals.quitFlag = false; + theGlobals.containerChanged = false; + theGlobals.result = noErr; + theGlobals.actualObjects = 0; + + /* here we go into recursion land... */ + FSIterateContainerLevel(&theGlobals); + result = theGlobals.result; + require_noerr(result, FSIterateContainerLevel); + +FSIterateContainerLevel: +NoIterateFilter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetDirectoryItems( + const FSRef *container, + FSRef ***refsHandle, /* pointer to handle of FSRefs */ + ItemCount *numRefs, + Boolean *containerChanged) +{ + /* Grab items 10 at a time. */ + enum { kMaxItemsPerBulkCall = 10 }; + + OSErr result; + OSErr memResult; + FSIterator iterator; + FSRef refs[kMaxItemsPerBulkCall]; + ItemCount actualObjects; + Boolean changed; + + /* check parameters */ + require_action((NULL != refsHandle) && (NULL != numRefs) && (NULL != containerChanged), + BadParameter, result = paramErr); + + *numRefs = 0; + *containerChanged = false; + *refsHandle = (FSRef **)NewHandle(0); + require_action(NULL != *refsHandle, NewHandle, result = memFullErr); + + /* open an FSIterator */ + result = FSOpenIterator(container, kFSIterateFlat, &iterator); + require_noerr(result, FSOpenIterator); + + /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ + do + { + result = FSGetCatalogInfoBulk(iterator, kMaxItemsPerBulkCall, &actualObjects, + &changed, kFSCatInfoNone, NULL, refs, NULL, NULL); + + /* if the container changed, set containerChanged for output, but keep going */ + if ( changed ) + { + *containerChanged = changed; + } + + /* any result other than noErr and errFSNoMoreItems is serious */ + require((noErr == result) || (errFSNoMoreItems == result), FSGetCatalogInfoBulk); + + /* add objects to output array and count */ + if ( 0 != actualObjects ) + { + /* concatenate the FSRefs to the end of the handle */ + PtrAndHand(refs, (Handle)*refsHandle, actualObjects * sizeof(FSRef)); + memResult = MemError(); + require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); + + *numRefs += actualObjects; + } + } while ( noErr == result ); + + verify_noerr(FSCloseIterator(iterator)); /* closing an open iterator should never fail, but... */ + + return ( noErr ); + + /**********************/ + +MemoryAllocationFailed: +FSGetCatalogInfoBulk: + + /* close the iterator */ + verify_noerr(FSCloseIterator(iterator)); + +FSOpenIterator: + /* dispose of handle if already allocated and clear the outputs */ + if ( NULL != *refsHandle ) + { + DisposeHandle((Handle)*refsHandle); + *refsHandle = NULL; + } + *numRefs = 0; + +NewHandle: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +/* + The GenerateUniqueName function generates a HFSUniStr255 name that is + unique in both dir1 and dir2. + + startSeed --> A pointer to a long which is used to generate the + unique name. + <-- It is modified on output to a value which should + be used to generate the next unique name. + dir1 --> The first directory. + dir2 --> The second directory. + uniqueName <-- A pointer to a HFSUniStr255 where the unique name + is to be returned. +*/ + +static +OSErr +GenerateUniqueHFSUniStr( + long *startSeed, + const FSRef *dir1, + const FSRef *dir2, + HFSUniStr255 *uniqueName) +{ + OSErr result; + long i; + FSRefParam pb; + FSRef newRef; + unsigned char hexStr[17] = "0123456789ABCDEF"; + + /* set up the parameter block */ + pb.name = uniqueName->unicode; + pb.nameLength = 8; /* always 8 characters */ + pb.textEncodingHint = kTextEncodingUnknown; + pb.newRef = &newRef; + + /* loop until we get fnfErr with a filename in both directories */ + result = noErr; + while ( fnfErr != result ) + { + /* convert startSeed to 8 character Unicode string */ + uniqueName->length = 8; + for ( i = 0; i < 8; ++i ) + { + uniqueName->unicode[i] = hexStr[((*startSeed >> ((7-i)*4)) & 0xf)]; + } + + /* try in dir1 */ + pb.ref = dir1; + result = PBMakeFSRefUnicodeSync(&pb); + if ( fnfErr == result ) + { + /* try in dir2 */ + pb.ref = dir2; + result = PBMakeFSRefUnicodeSync(&pb); + if ( fnfErr != result ) + { + /* exit if anything other than noErr or fnfErr */ + require_noerr(result, Dir2PBMakeFSRefUnicodeSyncFailed); + } + } + else + { + /* exit if anything other than noErr or fnfErr */ + require_noerr(result, Dir1PBMakeFSRefUnicodeSyncFailed); + } + + /* increment seed for next pass through loop, */ + /* or for next call to GenerateUniqueHFSUniStr */ + ++(*startSeed); + } + + /* we have a unique file name which doesn't exist in dir1 or dir2 */ + result = noErr; + +Dir2PBMakeFSRefUnicodeSyncFailed: +Dir1PBMakeFSRefUnicodeSyncFailed: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSExchangeObjectsCompat( + const FSRef *sourceRef, + const FSRef *destRef, + FSRef *newSourceRef, + FSRef *newDestRef) +{ + enum + { + /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */ + kGetCatInformationMask = (kFSCatInfoSettableInfo | + kFSCatInfoVolume | + kFSCatInfoParentDirID) & + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod), + /* set everything possible except for mod dates */ + kSetCatinformationMask = kFSCatInfoSettableInfo & + ~(kFSCatInfoContentMod | kFSCatInfoAttrMod) + }; + + OSErr result; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + FSCatalogInfo sourceCatalogInfo; /* source file's catalog information */ + FSCatalogInfo destCatalogInfo; /* destination file's catalog information */ + HFSUniStr255 sourceName; /* source file's Unicode name */ + HFSUniStr255 destName; /* destination file's Unicode name */ + FSRef sourceCurrentRef; /* FSRef to current location of source file throughout this function */ + FSRef destCurrentRef; /* FSRef to current location of destination file throughout this function */ + FSRef sourceParentRef; /* FSRef to parent directory of source file */ + FSRef destParentRef; /* FSRef to parent directory of destination file */ + HFSUniStr255 sourceUniqueName; /* unique name given to source file while exchanging it with destination */ + HFSUniStr255 destUniqueName; /* unique name given to destination file while exchanging it with source */ + long theSeed; /* the seed for generating unique names */ + Boolean sameParentDirs; /* true if source and destinatin parent directory is the same */ + + /* check parameters */ + require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr); + + /* output refs and current refs = input refs to start with */ + BlockMoveData(sourceRef, newSourceRef, sizeof(FSRef)); + BlockMoveData(sourceRef, &sourceCurrentRef, sizeof(FSRef)); + + BlockMoveData(destRef, newDestRef, sizeof(FSRef)); + BlockMoveData(destRef, &destCurrentRef, sizeof(FSRef)); + + /* get source volume's vRefNum */ + result = FSGetCatalogInfo(&sourceCurrentRef, kFSCatInfoVolume, &sourceCatalogInfo, NULL, NULL, NULL); + require_noerr(result, DetermineSourceVRefNumFailed); + + /* see if that volume supports FSExchangeObjects */ + result = FSGetVolParms(sourceCatalogInfo.volume, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + if ( (noErr == result) && VolSupportsFSExchangeObjects(&volParmsInfo) ) + { + /* yes - use FSExchangeObjects */ + result = FSExchangeObjects(sourceRef, destRef); + } + else + { + /* no - emulate FSExchangeObjects */ + + /* Note: The compatibility case won't work for files with *Btree control blocks. */ + /* Right now the only *Btree files are created by the system. */ + + /* get all catalog information and Unicode names for each file */ + result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef); + require_noerr(result, SourceFSGetCatalogInfoFailed); + + result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef); + require_noerr(result, DestFSGetCatalogInfoFailed); + + /* make sure source and destination are on same volume */ + require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* make sure both files are *really* files */ + require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) && + (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr); + + /* generate 2 names that are unique in both directories */ + theSeed = 0x4a696d4c; /* a fine unlikely filename */ + + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName); + require_noerr(result, GenerateUniqueHFSUniStr1Failed); + + result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName); + require_noerr(result, GenerateUniqueHFSUniStr2Failed); + + /* rename sourceCurrentRef to sourceUniqueName */ + result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef); + require_noerr(result, FSRenameUnicode1Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* rename destCurrentRef to destUniqueName */ + result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef); + require_noerr(result, FSRenameUnicode2Failed); + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + + /* are the source and destination parent directories the same? */ + sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID ); + if ( !sameParentDirs ) + { + /* move source file to dest parent directory */ + result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef); + require_noerr(result, FSMoveObject1Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* move dest file to source parent directory */ + result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef); + require_noerr(result, FSMoveObject2Failed); + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + } + + /* At this point, the files are in their new locations (if they were moved). */ + /* The source file is named sourceUniqueName and is in the directory referred to */ + /* by destParentRef. The destination file is named destUniqueName and is in the */ + /* directory referred to by sourceParentRef. */ + + /* give source file the dest file's catalog information except for mod dates */ + result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo); + require_noerr(result, FSSetCatalogInfo1Failed); + + /* give dest file the source file's catalog information except for mod dates */ + result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo); + require_noerr(result, FSSetCatalogInfo2Failed); + + /* rename source file with dest file's name */ + result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef); + require_noerr(result, FSRenameUnicode3Failed); + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + + /* rename dest file with source file's name */ + result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef); + require_noerr(result, FSRenameUnicode4Failed); + + /* we're done with no errors, so swap newSourceRef and newDestRef */ + BlockMoveData(newDestRef, newSourceRef, sizeof(FSRef)); + BlockMoveData(&sourceCurrentRef, newDestRef, sizeof(FSRef)); + } + + return ( result ); + + /**********************/ + +/* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */ +/* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */ +/* state and location they ended up in so that both files can be found by the calling code. */ + +FSRenameUnicode4Failed: + + /* attempt to rename source file to sourceUniqueName */ + if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) ) + { + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + } + +FSRenameUnicode3Failed: + + /* attempt to restore dest file's catalog information */ + verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo)); + +FSSetCatalogInfo2Failed: + + /* attempt to restore source file's catalog information */ + verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo)); + +FSSetCatalogInfo1Failed: + + if ( !sameParentDirs ) + { + /* attempt to move dest file back to dest directory */ + if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) ) + { + BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); + } + } + +FSMoveObject2Failed: + + if ( !sameParentDirs ) + { + /* attempt to move source file back to source directory */ + if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) ) + { + BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); + } + } + +FSMoveObject1Failed: + + /* attempt to rename dest file to original name */ + verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef)); + +FSRenameUnicode2Failed: + + /* attempt to rename source file to original name */ + verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef)); + +FSRenameUnicode1Failed: +GenerateUniqueHFSUniStr2Failed: +GenerateUniqueHFSUniStr1Failed: +NotAFile: +NotSameVolume: +DestFSGetCatalogInfoFailed: +SourceFSGetCatalogInfoFailed: +DetermineSourceVRefNumFailed: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Shared Environment Routines ----- + +/*****************************************************************************/ + +OSErr +FSLockRange( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart) +{ + OSErr result; + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + result = PBLockRangeSync(&pb); + require_noerr(result, PBLockRangeSync); + +PBLockRangeSync: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSUnlockRange( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart) +{ + OSErr result; + ParamBlockRec pb; + + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioReqCount = rangeLength; + pb.ioParam.ioPosMode = fsFromStart; + pb.ioParam.ioPosOffset = rangeStart; + result = PBUnlockRangeSync(&pb); + require_noerr(result, PBUnlockRangeSync); + +PBUnlockRangeSync: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetDirAccess( + const FSRef *ref, + SInt32 *ownerID, /* can be NULL */ + SInt32 *groupID, /* can be NULL */ + SInt32 *accessRights) /* can be NULL */ +{ + OSErr result; + FSSpec spec; + HParamBlockRec pb; + + /* get FSSpec from FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* get directory access info for FSSpec */ + pb.accessParam.ioNamePtr = (StringPtr)spec.name; + pb.accessParam.ioVRefNum = spec.vRefNum; + pb.fileParam.ioDirID = spec.parID; + result = PBHGetDirAccessSync(&pb); + require_noerr(result, PBHGetDirAccessSync); + + /* return the IDs and access rights */ + if ( NULL != ownerID ) + { + *ownerID = pb.accessParam.ioACOwnerID; + } + if ( NULL != groupID ) + { + *groupID = pb.accessParam.ioACGroupID; + } + if ( NULL != accessRights ) + { + *accessRights = pb.accessParam.ioACAccess; + } + +PBHGetDirAccessSync: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSSetDirAccess( + const FSRef *ref, + SInt32 ownerID, + SInt32 groupID, + SInt32 accessRights) +{ + OSErr result; + FSSpec spec; + HParamBlockRec pb; + + enum + { + /* Just the bits that can be set */ + kSetDirAccessSettableMask = (kioACAccessBlankAccessMask + + kioACAccessEveryoneWriteMask + kioACAccessEveryoneReadMask + kioACAccessEveryoneSearchMask + + kioACAccessGroupWriteMask + kioACAccessGroupReadMask + kioACAccessGroupSearchMask + + kioACAccessOwnerWriteMask + kioACAccessOwnerReadMask + kioACAccessOwnerSearchMask) + }; + + /* get FSSpec from FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* set directory access info for FSSpec */ + pb.accessParam.ioNamePtr = (StringPtr)spec.name; + pb.accessParam.ioVRefNum = spec.vRefNum; + pb.fileParam.ioDirID = spec.parID; + pb.accessParam.ioACOwnerID = ownerID; + pb.accessParam.ioACGroupID = groupID; + pb.accessParam.ioACAccess = accessRights & kSetDirAccessSettableMask; + result = PBHSetDirAccessSync(&pb); + require_noerr(result, PBHSetDirAccessSync); + +PBHSetDirAccessSync: +FSGetCatalogInfo: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolMountInfoSize( + FSVolumeRefNum volRefNum, + SInt16 *size) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != size, BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)size; + result = PBGetVolMountInfoSize(&pb); + require_noerr(result, PBGetVolMountInfoSize); + +PBGetVolMountInfoSize: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSGetVolMountInfo( + FSVolumeRefNum volRefNum, + void *volMountInfo) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != volMountInfo, BadParameter, result = paramErr); + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volRefNum; + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + result = PBGetVolMountInfo(&pb); + require_noerr(result, PBGetVolMountInfo); + +PBGetVolMountInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSVolumeMount( + const void *volMountInfo, + FSVolumeRefNum *volRefNum) +{ + OSErr result; + ParamBlockRec pb; + + /* check parameters */ + require_action(NULL != volRefNum, BadParameter, result = paramErr); + + pb.ioParam.ioBuffer = (Ptr)volMountInfo; + result = PBVolumeMount(&pb); + require_noerr(result, PBVolumeMount); + + /* return the volume reference number */ + *volRefNum = pb.ioParam.ioVRefNum; + +PBVolumeMount: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMapID( + FSVolumeRefNum volRefNum, + SInt32 ugID, + SInt16 objType, + Str31 name) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action(NULL != name, BadParameter, result = paramErr); + + pb.objParam.ioNamePtr = NULL; + pb.objParam.ioVRefNum = volRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = name; + pb.objParam.ioObjID = ugID; + result = PBHMapIDSync(&pb); + require_noerr(result, PBHMapIDSync); + +PBHMapIDSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMapName( + FSVolumeRefNum volRefNum, + ConstStr255Param name, + SInt16 objType, + SInt32 *ugID) +{ + OSErr result; + HParamBlockRec pb; + + /* check parameters */ + require_action(NULL != ugID, BadParameter, result = paramErr); + + pb.objParam.ioNamePtr = NULL; + pb.objParam.ioVRefNum = volRefNum; + pb.objParam.ioObjType = objType; + pb.objParam.ioObjNamePtr = (StringPtr)name; + result = PBHMapNameSync(&pb); + require_noerr(result, PBHMapNameSync); + + /* return the user or group ID */ + *ugID = pb.objParam.ioObjID; + +PBHMapNameSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCopyFile( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *copyName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* can be NULL */ +{ + OSErr result; + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + HParamBlockRec pb; + Str31 hfsName; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + + /* get source FSSpec from source FSRef */ + result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + require_noerr(result, FSGetCatalogInfo_srcFileRef); + + /* Make sure the volume supports CopyFile */ + result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + require_action((noErr == result) && VolHasCopyFile(&volParmsInfo), + NoCopyFileSupport, result = paramErr); + + /* get destination volume reference number and destination directory ID from destination FSRef */ + result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); + + /* tell the server to copy the object */ + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioDstVRefNum = catalogInfo.volume; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if ( NULL != copyName ) + { + result = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName); + require_noerr(result, UnicodeNameGetHFSName); + + pb.copyParam.ioCopyName = hfsName; + } + else + { + pb.copyParam.ioCopyName = NULL; + } + result = PBHCopyFileSync(&pb); + require_noerr(result, PBHCopyFileSync); + + if ( NULL != newRef ) + { + verify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, + pb.copyParam.ioCopyName, newRef)); + } + +PBHCopyFileSync: +UnicodeNameGetHFSName: +FSGetCatalogInfo_dstDirectoryRef: +NoCopyFileSupport: +FSGetCatalogInfo_srcFileRef: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSMoveRename( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *moveName, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef) /* can be NULL */ +{ + OSErr result; + FSSpec srcFileSpec; + FSCatalogInfo catalogInfo; + HParamBlockRec pb; + Str31 hfsName; + GetVolParmsInfoBuffer volParmsInfo; + UInt32 infoSize; + + /* get source FSSpec from source FSRef */ + result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); + require_noerr(result, FSGetCatalogInfo_srcFileRef); + + /* Make sure the volume supports MoveRename */ + result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), + &volParmsInfo, &infoSize); + require_action((noErr == result) && VolHasMoveRename(&volParmsInfo), + NoMoveRenameSupport, result = paramErr); + + /* get destination volume reference number and destination directory ID from destination FSRef */ + result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); + + /* make sure the source and destination are on the same volume */ + require_action(srcFileSpec.vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); + + /* tell the server to move and rename the object */ + pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; + pb.copyParam.ioDirID = srcFileSpec.parID; + pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; + pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; + pb.copyParam.ioNewName = NULL; + if ( NULL != moveName ) + { + result = UnicodeNameGetHFSName(nameLength, moveName, textEncodingHint, false, hfsName); + require_noerr(result, UnicodeNameGetHFSName); + + pb.copyParam.ioCopyName = hfsName; + } + else + { + pb.copyParam.ioCopyName = NULL; + } + result = PBHMoveRenameSync(&pb); + require_noerr(result, PBHMoveRenameSync); + + if ( NULL != newRef ) + { + verify_noerr(FSMakeFSRef(pb.copyParam.ioVRefNum, pb.copyParam.ioNewDirID, + pb.copyParam.ioCopyName, newRef)); + } + +PBHMoveRenameSync: +UnicodeNameGetHFSName: +NotSameVolume: +FSGetCatalogInfo_dstDirectoryRef: +NoMoveRenameSupport: +FSGetCatalogInfo_srcFileRef: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- File ID Routines ----- + +/*****************************************************************************/ + +OSErr +FSResolveFileIDRef( + FSVolumeRefNum volRefNum, + SInt32 fileID, + FSRef *ref) +{ + OSErr result; + FIDParam pb; + Str255 tempStr; + + /* check parameters */ + require_action(NULL != ref, BadParameter, result = paramErr); + + /* resolve the file ID reference */ + tempStr[0] = 0; + pb.ioNamePtr = tempStr; + pb.ioVRefNum = volRefNum; + pb.ioFileID = fileID; + result = PBResolveFileIDRefSync((HParmBlkPtr)&pb); + require_noerr(result, PBResolveFileIDRefSync); + + /* and then make an FSRef to the file */ + result = FSMakeFSRef(volRefNum, pb.ioSrcDirID, tempStr, ref); + require_noerr(result, FSMakeFSRef); + +FSMakeFSRef: +PBResolveFileIDRefSync: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSCreateFileIDRef( + const FSRef *ref, + SInt32 *fileID) +{ + OSErr result; + FSSpec spec; + FIDParam pb; + + /* check parameters */ + require_action(NULL != fileID, BadParameter, result = paramErr); + + /* Get an FSSpec from the FSRef */ + result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Create (or get) the file ID reference using the FSSpec */ + pb.ioNamePtr = (StringPtr)spec.name; + pb.ioVRefNum = spec.vRefNum; + pb.ioSrcDirID = spec.parID; + result = PBCreateFileIDRefSync((HParmBlkPtr)&pb); + require((noErr == result) || (fidExists == result) || (afpIDExists == result), + PBCreateFileIDRefSync); + + /* return the file ID reference */ + *fileID = pb.ioFileID; + +PBCreateFileIDRefSync: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +#pragma mark ----- Utility Routines ----- + +/*****************************************************************************/ + +Ptr +GetTempBuffer( + ByteCount buffReqSize, + ByteCount *buffActSize) +{ + enum + { + kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */ + }; + + Ptr tempPtr; + + /* check parameters */ + require_action(NULL != buffActSize, BadParameter, tempPtr = NULL); + + /* Make request a multiple of 4K bytes */ + buffReqSize = buffReqSize & 0xfffff000; + + if ( buffReqSize < 0x00001000 ) + { + /* Request was smaller than 4K bytes - make it 4K */ + buffReqSize = 0x00001000; + } + + /* Attempt to allocate the memory */ + tempPtr = NewPtr(buffReqSize); + + /* If request failed, go to backup plan */ + if ( (tempPtr == NULL) && (buffReqSize > 0x00001000) ) + { + /* + ** Try to get largest 4K byte block available + ** leaving some slop for the toolbox if possible + */ + long freeMemory = (FreeMem() - kSlopMemory) & 0xfffff000; + + buffReqSize = MaxBlock() & 0xfffff000; + + if ( buffReqSize > freeMemory ) + { + buffReqSize = freeMemory; + } + + if ( buffReqSize == 0 ) + { + buffReqSize = 0x00001000; + } + + tempPtr = NewPtr(buffReqSize); + } + + /* Return bytes allocated */ + if ( tempPtr != NULL ) + { + *buffActSize = buffReqSize; + } + else + { + *buffActSize = 0; + } + +BadParameter: + + return ( tempPtr ); +} + +/*****************************************************************************/ + +OSErr +FileRefNumGetFSRef( + short refNum, + FSRef *ref) +{ + return ( FSGetForkCBInfo(refNum, 0, NULL, NULL, NULL, ref, NULL) ); +} + +/*****************************************************************************/ + +OSErr +FSSetDefault( + const FSRef *newDefault, + FSRef *oldDefault) +{ + OSErr result; + FSVolumeRefNum vRefNum; + long dirID; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action((NULL != newDefault) && (NULL != oldDefault), BadParameter, result = paramErr); + + /* Get nodeFlags, vRefNum and dirID (nodeID) of newDefault */ + result = FSGetCatalogInfo(newDefault, + kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Make sure newDefault is a directory */ + require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), NewDefaultNotDirectory, + result = dirNFErr); + + /* Get the current working directory. */ + result = HGetVol(NULL, &vRefNum, &dirID); + require_noerr(result, HGetVol); + + /* Return the oldDefault FSRef */ + result = FSMakeFSRef(vRefNum, dirID, NULL, oldDefault); + require_noerr(result, FSMakeFSRef); + + /* Set the new current working directory */ + result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); + require_noerr(result, HSetVol); + +HSetVol: +FSMakeFSRef: +HGetVol: +NewDefaultNotDirectory: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ + +OSErr +FSRestoreDefault( + const FSRef *oldDefault) +{ + OSErr result; + FSCatalogInfo catalogInfo; + + /* check parameters */ + require_action(NULL != oldDefault, BadParameter, result = paramErr); + + /* Get nodeFlags, vRefNum and dirID (nodeID) of oldDefault */ + result = FSGetCatalogInfo(oldDefault, + kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, + &catalogInfo, NULL, NULL, NULL); + require_noerr(result, FSGetCatalogInfo); + + /* Make sure oldDefault is a directory */ + require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), OldDefaultNotDirectory, + result = dirNFErr); + + /* Set the current working directory to oldDefault */ + result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); + require_noerr(result, HSetVol); + +HSetVol: +OldDefaultNotDirectory: +FSGetCatalogInfo: +BadParameter: + + return ( result ); +} + +/*****************************************************************************/ diff --git a/src/osx/carbon/morefilex/MoreFilesX.h b/src/osx/carbon/morefilex/MoreFilesX.h new file mode 100644 index 0000000000..b61da102e1 --- /dev/null +++ b/src/osx/carbon/morefilex/MoreFilesX.h @@ -0,0 +1,1825 @@ +/* + File: MoreFilesX.h + + Contains: A collection of useful high-level File Manager routines + which use the HFS Plus APIs wherever possible. + + Version: MoreFilesX 1.0.1 + + Copyright: © 1992-2002 by Apple Computer, Inc., all rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + File Ownership: + + DRI: Apple Macintosh Developer Technical Support + + Other Contact: For bug reports, consult the following page on + the World Wide Web: + http://developer.apple.com/bugreporter/ + + Technology: DTS Sample Code + + Writers: + + (JL) Jim Luther + + Change History (most recent first): + + <3> 4/19/02 JL [2853905] Fixed #if test around header includes. + <2> 4/19/02 JL [2853901] Updated standard disclaimer. + <1> 1/25/02 JL MoreFilesX 1.0 + + Notes: + What do those arrows in the documentation for each routine mean? + + --> The parameter is an input + + <-- The parameter is an output. The pointer to the variable + where the output will be returned (must not be NULL). + + <** The parameter is an optional output. If it is not a + NULL pointer, it points to the variable where the output + will be returned. If it is a NULL pointer, the output will + not be returned and will possibly let the routine and the + File Manager do less work. If you don't need an optional output, + don't ask for it. + **> The parameter is an optional input. If it is not a + NULL pointer, it points to the variable containing the + input data. If it is a NULL pointer, the input is not used + and will possibly let the routine and the File Manager + do less work. +*/ + +#ifndef __MOREFILESX__ +#define __MOREFILESX__ + +#ifndef __CARBON__ + #if defined(__MACH__) + #include + #else + #include + #endif +#endif + +#if PRAGMA_ONCE +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if PRAGMA_IMPORT +#pragma import on +#endif + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(push, 2) +#elif PRAGMA_STRUCT_PACK + #pragma pack(2) +#endif + +/*****************************************************************************/ + +#pragma mark ----- FinderInfo and ExtendedFinderInfo ----- + +/* + * FSGetFinderInfo and FSSetFinderInfo use these unions for Finder information. + */ + +union FinderInfo +{ + FileInfo file; + FolderInfo folder; +}; +typedef union FinderInfo FinderInfo; + +union ExtendedFinderInfo +{ + ExtendedFileInfo file; + ExtendedFolderInfo folder; +}; +typedef union ExtendedFinderInfo ExtendedFinderInfo; + +/*****************************************************************************/ + +#pragma mark ----- GetVolParmsInfoBuffer Macros ----- + +/* + * Macros to get information out of GetVolParmsInfoBuffer. + */ + +/* version 1 field getters */ +#define GetVolParmsInfoVersion(volParms) \ + ((volParms)->vMVersion) +#define GetVolParmsInfoAttrib(volParms) \ + ((volParms)->vMAttrib) +#define GetVolParmsInfoLocalHand(volParms) \ + ((volParms)->vMLocalHand) +#define GetVolParmsInfoServerAdr(volParms) \ + ((volParms)->vMServerAdr) + +/* version 2 field getters (assume zero result if version < 2) */ +#define GetVolParmsInfoVolumeGrade(volParms) \ + (((volParms)->vMVersion >= 2) ? (volParms)->vMVolumeGrade : 0) +#define GetVolParmsInfoForeignPrivID(volParms) \ + (((volParms)->vMVersion >= 2) ? (volParms)->vMForeignPrivID : 0) + +/* version 3 field getters (assume zero result if version < 3) */ +#define GetVolParmsInfoExtendedAttributes(volParms) \ + (((volParms)->vMVersion >= 3) ? (volParms)->vMExtendedAttributes : 0) + +/* attribute bits supported by all versions of GetVolParmsInfoBuffer */ +#define VolIsNetworkVolume(volParms) \ + ((volParms)->vMServerAdr != 0) +#define VolHasLimitFCBs(volParms) \ + (((volParms)->vMAttrib & (1L << bLimitFCBs)) != 0) +#define VolHasLocalWList(volParms) \ + (((volParms)->vMAttrib & (1L << bLocalWList)) != 0) +#define VolHasNoMiniFndr(volParms) \ + (((volParms)->vMAttrib & (1L << bNoMiniFndr)) != 0) +#define VolHasNoVNEdit(volParms) \ + (((volParms)->vMAttrib & (1L << bNoVNEdit)) != 0) +#define VolHasNoLclSync(volParms) \ + (((volParms)->vMAttrib & (1L << bNoLclSync)) != 0) +#define VolHasTrshOffLine(volParms) \ + (((volParms)->vMAttrib & (1L << bTrshOffLine)) != 0) +#define VolHasNoSwitchTo(volParms) \ + (((volParms)->vMAttrib & (1L << bNoSwitchTo)) != 0) +#define VolHasNoDeskItems(volParms) \ + (((volParms)->vMAttrib & (1L << bNoDeskItems)) != 0) +#define VolHasNoBootBlks(volParms) \ + (((volParms)->vMAttrib & (1L << bNoBootBlks)) != 0) +#define VolHasAccessCntl(volParms) \ + (((volParms)->vMAttrib & (1L << bAccessCntl)) != 0) +#define VolHasNoSysDir(volParms) \ + (((volParms)->vMAttrib & (1L << bNoSysDir)) != 0) +#define VolHasExtFSVol(volParms) \ + (((volParms)->vMAttrib & (1L << bHasExtFSVol)) != 0) +#define VolHasOpenDeny(volParms) \ + (((volParms)->vMAttrib & (1L << bHasOpenDeny)) != 0) +#define VolHasCopyFile(volParms) \ + (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0) +#define VolHasMoveRename(volParms) \ + (((volParms)->vMAttrib & (1L << bHasMoveRename)) != 0) +#define VolHasDesktopMgr(volParms) \ + (((volParms)->vMAttrib & (1L << bHasDesktopMgr)) != 0) +#define VolHasShortName(volParms) \ + (((volParms)->vMAttrib & (1L << bHasShortName)) != 0) +#define VolHasFolderLock(volParms) \ + (((volParms)->vMAttrib & (1L << bHasFolderLock)) != 0) +#define VolHasPersonalAccessPrivileges(volParms) \ + (((volParms)->vMAttrib & (1L << bHasPersonalAccessPrivileges)) != 0) +#define VolHasUserGroupList(volParms) \ + (((volParms)->vMAttrib & (1L << bHasUserGroupList)) != 0) +#define VolHasCatSearch(volParms) \ + (((volParms)->vMAttrib & (1L << bHasCatSearch)) != 0) +#define VolHasFileIDs(volParms) \ + (((volParms)->vMAttrib & (1L << bHasFileIDs)) != 0) +#define VolHasBTreeMgr(volParms) \ + (((volParms)->vMAttrib & (1L << bHasBTreeMgr)) != 0) +#define VolHasBlankAccessPrivileges(volParms) \ + (((volParms)->vMAttrib & (1L << bHasBlankAccessPrivileges)) != 0) +#define VolSupportsAsyncRequests(volParms) \ + (((volParms)->vMAttrib & (1L << bSupportsAsyncRequests)) != 0) +#define VolSupportsTrashVolumeCache(volParms) \ + (((volParms)->vMAttrib & (1L << bSupportsTrashVolumeCache)) != 0) + +/* attribute bits supported by version 3 and greater versions of GetVolParmsInfoBuffer */ +#define VolIsEjectable(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsEjectable)) != 0) +#define VolSupportsHFSPlusAPIs(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsHFSPlusAPIs)) != 0) +#define VolSupportsFSCatalogSearch(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSCatalogSearch)) != 0) +#define VolSupportsFSExchangeObjects(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSExchangeObjects)) != 0) +#define VolSupports2TBFiles(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupports2TBFiles)) != 0) +#define VolSupportsLongNames(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsLongNames)) != 0) +#define VolSupportsMultiScriptNames(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsMultiScriptNames)) != 0) +#define VolSupportsNamedForks(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsNamedForks)) != 0) +#define VolSupportsSubtreeIterators(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSubtreeIterators)) != 0) +#define VolL2PCanMapFileBlocks(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bL2PCanMapFileBlocks)) != 0) +#define VolParentModDateChanges(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bParentModDateChanges)) != 0) +#define VolAncestorModDateChanges(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bAncestorModDateChanges)) != 0) +#define VolSupportsSymbolicLinks(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSymbolicLinks)) != 0) +#define VolIsAutoMounted(volParms) \ + ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsAutoMounted)) != 0) + +/*****************************************************************************/ + +#pragma mark ----- userPrivileges Bit Masks and Macros ----- + +/* + * Bit masks and macros to get common information out of userPrivileges byte + * returned by FSGetCatalogInfo. + * + * Note: The userPrivileges byte is the same as the ioACUser byte returned + * by PBGetCatInfo, and is the 1's complement of the user's privileges + * byte returned in ioACAccess by PBHGetDirAccess. That's where the + * ioACUser names came from. + * + * The userPrivileges are user's effective privileges based on the + * user ID and the groups that user belongs to, and the owner, group, + * and everyone privileges for the given directory. + */ + +enum +{ + /* mask for just the access restriction bits */ + kioACUserAccessMask = (kioACUserNoSeeFolderMask + + kioACUserNoSeeFilesMask + + kioACUserNoMakeChangesMask), + /* common access privilege settings */ + kioACUserFull = 0x00, /* no access restiction bits on */ + kioACUserNone = kioACUserAccessMask, /* all access restiction bits on */ + kioACUserDropBox = (kioACUserNoSeeFolderMask + + kioACUserNoSeeFilesMask), /* make changes, but not see files or folders */ + kioACUserBulletinBoard = kioACUserNoMakeChangesMask /* see files and folders, but not make changes */ +}; + + +/* Macros for testing ioACUser bits. */ + +#define UserIsOwner(userPrivileges) \ + (((userPrivileges) & kioACUserNotOwnerMask) == 0) +#define UserHasFullAccess(userPrivileges) \ + (((userPrivileges) & (kioACUserAccessMask)) == kioACUserFull) +#define UserHasDropBoxAccess(userPrivileges) \ + (((userPrivileges) & kioACUserAccessMask) == kioACUserDropBox) +#define UserHasBulletinBoard(userPrivileges) \ + (((userPrivileges) & kioACUserAccessMask) == kioACUserBulletinBoard) +#define UserHasNoAccess(userPrivileges) \ + (((userPrivileges) & kioACUserAccessMask) == kioACUserNone) + +/*****************************************************************************/ + +#pragma mark ----- File Access Routines ----- + +/*****************************************************************************/ + +#pragma mark FSCopyFork + +OSErr +FSCopyFork( + SInt16 srcRefNum, + SInt16 dstRefNum, + void *copyBufferPtr, + ByteCount copyBufferSize); + +/* + The FSCopyFork function copies all data from the source fork to the + destination fork of open file forks and makes sure the destination EOF + is equal to the source EOF. + + srcRefNum --> The source file reference number. + dstRefNum --> The destination file reference number. + copyBufferPtr --> Pointer to buffer to use during copy. The + buffer should be at least 4K-bytes minimum. + The larger the buffer, the faster the copy + (up to a point). + copyBufferSize --> The size of the copy buffer. +*/ + +/*****************************************************************************/ + +#pragma mark ----- Volume Access Routines ----- + +/*****************************************************************************/ + +#pragma mark FSGetVolParms + +OSErr +FSGetVolParms( + FSVolumeRefNum volRefNum, + UInt32 bufferSize, + GetVolParmsInfoBuffer *volParmsInfo, + UInt32 *actualInfoSize); + +/* + The FSGetVolParms function returns information about the characteristics + of a volume. A result of paramErr usually just means the volume doesn't + support GetVolParms and the feature you were going to check + for isn't available. + + volRefNum --> Volume specification. + bufferSize --> Size of buffer pointed to by volParmsInfo. + volParmsInfo <-- A GetVolParmsInfoBuffer record where the volume + attributes information is returned. + actualInfoSize <-- The number of bytes actually returned + in volParmsInfo. + + __________ + + Also see: The GetVolParmsInfoBuffer Macros for checking attribute bits + in this file +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVRefNum + +OSErr +FSGetVRefNum( + const FSRef *ref, + FSVolumeRefNum *vRefNum); + +/* + The FSGetVRefNum function determines the volume reference + number of a volume from a FSRef. + + ref --> The FSRef. + vRefNum <-- The volume reference number. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVInfo + +OSErr +FSGetVInfo( + FSVolumeRefNum volume, + HFSUniStr255 *volumeName, /* can be NULL */ + UInt64 *freeBytes, /* can be NULL */ + UInt64 *totalBytes); /* can be NULL */ + +/* + The FSGetVInfo function returns the name, available space (in bytes), + and total space (in bytes) for the specified volume. + + volume --> The volume reference number. + volumeName <** An optional pointer to a HFSUniStr255. + If not NULL, the volume name will be returned in + the HFSUniStr255. + freeBytes <** An optional pointer to a UInt64. + If not NULL, the number of free bytes on the + volume will be returned in the UInt64. + totalBytes <** An optional pointer to a UInt64. + If not NULL, the total number of bytes on the + volume will be returned in the UInt64. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVolFileSystemID + +OSErr +FSGetVolFileSystemID( + FSVolumeRefNum volume, + UInt16 *fileSystemID, /* can be NULL */ + UInt16 *signature); /* can be NULL */ + +/* + The FSGetVolFileSystemID function returns the file system ID and signature + of a mounted volume. The file system ID identifies the file system + that handles requests to a particular volume. The signature identifies the + volume type of the volume (for example, FSID 0 is Macintosh HFS Plus, HFS + or MFS, where a signature of 0x4244 identifies the volume as HFS). + Here's a partial list of file system ID numbers (only Apple's file systems + are listed): + FSID File System + ----- ----------------------------------------------------- + $0000 Macintosh HFS Plus, HFS or MFS + $0100 ProDOS File System + $0101 PowerTalk Mail Enclosures + $4147 ISO 9660 File Access (through Foreign File Access) + $4242 High Sierra File Access (through Foreign File Access) + $464D QuickTake File System (through Foreign File Access) + $4953 Macintosh PC Exchange (MS-DOS) + $4A48 Audio CD Access (through Foreign File Access) + $4D4B Apple Photo Access (through Foreign File Access) + $6173 AppleShare (later versions of AppleShare only) + + See the Technical Note "FL 35 - Determining Which File System + Is Active" and the "Guide to the File System Manager" for more + information. + + volume --> The volume reference number. + fileSystemID <** An optional pointer to a UInt16. + If not NULL, the volume's file system ID will + be returned in the UInt16. + signature <** An optional pointer to a UInt16. + If not NULL, the volume's signature will + be returned in the UInt16. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetMountedVolumes + +OSErr +FSGetMountedVolumes( + FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */ + ItemCount *numVolumes); + +/* + The FSGetMountedVolumes function returns the list of volumes currently + mounted in an array of FSRef records. The array of FSRef records is + returned in a Handle, volumeRefsHandle, which is allocated by + FSGetMountedVolumes. The caller is responsible for disposing of + volumeRefsHandle if the FSGetMountedVolumes returns noErr. + + volumeRefsHandle <-- Pointer to an FSRef Handle where the array of + FSRefs is to be returned. + numVolumes <-- The number of volumes returned in the array. +*/ + +/*****************************************************************************/ + +#pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines ----- + +/*****************************************************************************/ + +#pragma mark FSRefMakeFSSpec + +OSErr +FSRefMakeFSSpec( + const FSRef *ref, + FSSpec *spec); + +/* + The FSRefMakeFSSpec function returns an FSSpec for the file or + directory specified by the ref parameter. + + ref --> An FSRef specifying the file or directory. + spec <-- The FSSpec. +*/ + +/*****************************************************************************/ + +#pragma mark FSMakeFSRef + +OSErr +FSMakeFSRef( + FSVolumeRefNum volRefNum, + SInt32 dirID, + ConstStr255Param name, + FSRef *ref); + +/* + The FSMakeFSRef function creates an FSRef from the traditional + volume reference number, directory ID and pathname inputs. It is + functionally equivalent to FSMakeFSSpec followed by FSpMakeFSRef. + + volRefNum --> Volume specification. + dirID --> Directory specification. + name --> The file or directory name, or NULL. + ref <-- The FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark FSMakePath + +OSStatus +FSMakePath( + SInt16 vRefNum, + SInt32 dirID, + ConstStr255Param name, + UInt8 *path, + UInt32 maxPathSize); + +/* + The FSMakePath function creates a pathname from the traditional volume reference + number, directory ID, and pathname inputs. It is functionally equivalent to + FSMakeFSSpec, FSpMakeFSRef, FSRefMakePath. + + volRefNum --> Volume specification. + dirID --> Directory specification. + name --> The file or directory name, or NULL. + path <-- A pointer to a buffer which FSMakePath will + fill with a C string representing the pathname + to the file or directory specified. The format of + the pathname returned can be determined with the + Gestalt selector gestaltFSAttr's + gestaltFSUsesPOSIXPathsForConversion bit. + If the gestaltFSUsesPOSIXPathsForConversion bit is + clear, the pathname is a Mac OS File Manager full + pathname in a C string, and file or directory names + in the pathname may be mangled as returned by + the File Manager. If the + gestaltFSUsesPOSIXPathsForConversion bit is set, + the pathname is a UTF8 encoded POSIX absolute + pathname in a C string. In either case, the + pathname returned can be passed back to + FSPathMakeRef to create an FSRef to the file or + directory, or FSPathMakeFSSpec to craete an FSSpec + to the file or directory. + maxPathSize --> The size of the path buffer in bytes. If the path + buffer is too small for the pathname string, + FSMakePath returns pathTooLongErr or + buffersTooSmall. +*/ + +/*****************************************************************************/ + +#pragma mark FSPathMakeFSSpec + +OSStatus +FSPathMakeFSSpec( + const UInt8 *path, + FSSpec *spec, + Boolean *isDirectory); /* can be NULL */ + +/* + The FSPathMakeFSSpec function converts a pathname to an FSSpec. + + path --> A pointer to a C String that is the pathname. The + format of the pathname you must supply can be + determined with the Gestalt selector gestaltFSAttr's + gestaltFSUsesPOSIXPathsForConversion bit. + If the gestaltFSUsesPOSIXPathsForConversion bit is + clear, the pathname must be a Mac OS File Manager + full pathname in a C string. If the + gestaltFSUsesPOSIXPathsForConversion bit is set, + the pathname must be a UTF8 encoded POSIX absolute + pathname in a C string. + spec <-- The FSSpec. + isDirectory <** An optional pointer to a Boolean. + If not NULL, true will be returned in the Boolean + if the specified path is a directory, or false will + be returned in the Boolean if the specified path is + a file. +*/ + +/*****************************************************************************/ + +#pragma mark UnicodeNameGetHFSName + +OSErr +UnicodeNameGetHFSName( + UniCharCount nameLength, + const UniChar *name, + TextEncoding textEncodingHint, + Boolean isVolumeName, + Str31 hfsName); + +/* + The UnicodeNameGetHFSName function converts a Unicode string + to a Pascal Str31 (or Str27) string using an algorithm similar to that used + by the File Manager. Note that if the name is too long or cannot be converted + using the given text encoding hint, you will get an error instead of the + mangled name that the File Manager would return. + + nameLength --> Number of UniChar in name parameter. + name --> The Unicode string to convert. + textEncodingHint --> The text encoding hint used for the conversion. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + isVolumeName --> If true, the output name will be limited to + 27 characters (kHFSMaxVolumeNameChars). If false, + the output name will be limited to 31 characters + (kHFSMaxFileNameChars). + hfsName <-- The hfsName as a Pascal string. + + __________ + + Also see: HFSNameGetUnicodeName +*/ + +/*****************************************************************************/ + +#pragma mark HFSNameGetUnicodeName + +OSErr +HFSNameGetUnicodeName( + ConstStr31Param hfsName, + TextEncoding textEncodingHint, + HFSUniStr255 *unicodeName); + +/* + The HFSNameGetUnicodeName function converts a Pascal Str31 string to an + Unicode HFSUniStr255 string using the same routines as the File Manager. + + hfsName --> The Pascal string to convert. + textEncodingHint --> The text encoding hint used for the conversion. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + unicodeName <-- The Unicode string. + + __________ + + Also see: UnicodeNameGetHFSName +*/ + +/*****************************************************************************/ + +#pragma mark ----- File/Directory Manipulation Routines ----- + +/*****************************************************************************/ + +#pragma mark FSRefValid + +Boolean FSRefValid(const FSRef *ref); + +/* + The FSRefValid function determines if an FSRef is valid. If the result is + true, then the FSRef refers to an existing file or directory. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetParentRef + +OSErr +FSGetParentRef( + const FSRef *ref, + FSRef *parentRef); + +/* + The FSGetParentRef function gets the parent directory FSRef of the + specified object. + + Note: FSRefs always point to real file system objects. So, there cannot + be a FSRef to the parent of volume root directories. If you call + FSGetParentRef with a ref to the root directory of a volume, the + function result will be noErr and the parentRef will be invalid (using it + for other file system requests will fail). + + ref --> FSRef to a file or directory. + parentRef <-- The parent directory's FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetFileDirName + +OSErr +FSGetFileDirName( + const FSRef *ref, + HFSUniStr255 *outName); + +/* + The FSGetFileDirName function gets the name of the file or directory + specified. + + ref --> FSRef to a file or directory. + outName <-- The file or directory name. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetNodeID + +OSErr +FSGetNodeID( + const FSRef *ref, + long *nodeID, /* can be NULL */ + Boolean *isDirectory); /* can be NULL */ + +/* + The GetNodeIDFromFSRef function gets the node ID number of the + file or directory specified (note: the node ID is the directory ID + for directories). + + ref --> FSRef to a file or directory. + nodeID <** An optional pointer to a long. + If not NULL, the node ID will be returned in + the long. + isDirectory <** An optional pointer to a Boolean. + If not NULL, true will be returned in the Boolean + if the object is a directory, or false will be + returned in the Boolean if object is a file. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetUserPrivilegesPermissions + +OSErr +FSGetUserPrivilegesPermissions( + const FSRef *ref, + UInt8 *userPrivileges, /* can be NULL */ + UInt32 permissions[4]); /* can be NULL */ + +/* + The FSGetUserPrivilegesPermissions function gets the userPrivileges and/or + permissions of the file or directory specified. + + ref --> FSRef to a file or directory. + userPrivileges <** An optional pointer to a UInt8. + If not NULL, the userPrivileges will be returned + in the UInt8. + permissions <** An optional pointer to an UInt32[4] array. + If not NULL, the permissions will be returned + in the UInt32[4] array. +*/ + +/*****************************************************************************/ + +#pragma mark FSCheckLock + +OSErr +FSCheckLock( + const FSRef *ref); + +/* + The FSCheckLock function determines if a file or directory is locked. + If FSCheckLock returns noErr, then the file or directory is not locked + and the volume it is on is not locked either. If FSCheckLock returns + fLckdErr, then the file or directory is locked. If FSCheckLock returns + wPrErr, then the volume is locked by hardware (i.e., locked tab on + removable media). If FSCheckLock returns vLckdErr, then the volume is + locked by software. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetForkSizes + +OSErr +FSGetForkSizes( + const FSRef *ref, + UInt64 *dataLogicalSize, /* can be NULL */ + UInt64 *rsrcLogicalSize); /* can be NULL */ + +/* + The FSGetForkSizes returns the size of the data and/or resource fork for + the specified file. + + ref --> FSRef to a file or directory. + dataLogicalSize <** An optional pointer to a UInt64. + If not NULL, the data fork's size will be + returned in the UInt64. + rsrcLogicalSize <** An optional pointer to a UInt64. + If not NULL, the resource fork's size will be + returned in the UInt64. + + __________ + + Also see: FSGetTotalForkSizes +*/ + +/*****************************************************************************/ + +#pragma mark FSGetTotalForkSizes + +OSErr +FSGetTotalForkSizes( + const FSRef *ref, + UInt64 *totalLogicalSize, /* can be NULL */ + UInt64 *totalPhysicalSize, /* can be NULL */ + ItemCount *forkCount); /* can be NULL */ + +/* + The FSGetTotalForkSizes returns the total logical size and/or the total + physical size of the specified file (i.e., it adds the sizes of all file + forks). It optionally returns the number of file forks. + + ref --> FSRef to a file or directory. + totalLogicalSize <** An optional pointer to a UInt64. + If not NULL, the sum of all fork logical sizes + will be returned in the UInt64. + totalPhysicalSize <** An optional pointer to a UInt64. + If not NULL, the sum of all fork physical sizes + will be returned in the UInt64. + forkCount <** An optional pointer to a ItemCount. + If not NULL, the number of file forks + will be returned in the ItemCount. + + __________ + + Also see: FSGetForkSizes +*/ + +/*****************************************************************************/ + +#pragma mark FSBumpDate + +OSErr +FSBumpDate( + const FSRef *ref); + +/* + The FSBumpDate function changes the content modification date of a file + or directory to the current date/time. If the content modification date + is already equal to the current date/time, then add one second to the + content modification date. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetFinderInfo + +OSErr +FSGetFinderInfo( + const FSRef *ref, + FinderInfo *info, /* can be NULL */ + ExtendedFinderInfo *extendedInfo, /* can be NULL */ + Boolean *isDirectory); /* can be NULL */ + +/* + The FSGetFinderInfo function gets the finder information for a file or + directory. + + ref --> FSRef to a file or directory. + info <** An optional pointer to a FinderInfo. + If not NULL, the FileInfo (if ref is a file) or + the FolderInfo (if ref is a folder) will be + returned in the FinderInfo. + extendedInfo <** An optional pointer to a ExtendedFinderInfo. + If not NULL, the ExtendedFileInfo (if ref is a file) + or the ExtendedFolderInfo (if ref is a folder) will + be returned in the ExtendedFinderInfo. + isDirectory <** An optional pointer to a Boolean. + If not NULL, true will be returned in the Boolean + if the object is a directory, or false will be + returned in the Boolean if object is a file. + + __________ + + Also see: FSSetFinderInfo +*/ + +/*****************************************************************************/ + +#pragma mark FSSetFinderInfo + +OSErr +FSSetFinderInfo( + const FSRef *ref, + const FinderInfo *info, /* can be NULL */ + const ExtendedFinderInfo *extendedInfo); /* can be NULL */ + +/* + The FSSetFinderInfo function sets the finder information for a file or + directory. + + ref --> FSRef to a file or directory. + info **> A pointer to a FinderInfo record with the new + FileInfo (if ref is a file) or new FolderInfo + (if ref is a folder), or NULL if the FinderInfo + is not to be changed. + extendedInfo **> A pointer to a FinderInfo record with the new + ExtendedFileInfo (if ref is a file) or new + ExtendedFolderInfo (if ref is a folder), or NULL + if the ExtendedFinderInfo is not to be changed. + + __________ + + Also see: FSGetFinderInfo +*/ + +/*****************************************************************************/ + +#pragma mark FSChangeCreatorType + +OSErr +FSChangeCreatorType( + const FSRef *ref, + OSType fileCreator, + OSType fileType); + +/* + The FSChangeCreatorType function changes the creator and/or file type of a file. + + ref --> FSRef to a file. + creator --> The new creator type or 0x00000000 to leave + the creator type alone. + fileType --> The new file type or 0x00000000 to leave the + file type alone. +*/ + +/*****************************************************************************/ + +#pragma mark FSChangeFinderFlags + +OSErr +FSChangeFinderFlags( + const FSRef *ref, + Boolean setBits, + UInt16 flagBits); + +/* + The FSChangeFinderFlags function sets or clears flag bits in + the finderFlags field of a file's FileInfo record or a + directory's FolderInfo record. + + ref --> FSRef to a file or directory. + setBits --> If true, then set the bits specified in flagBits. + If false, then clear the bits specified in flagBits. + flagBits --> The flagBits parameter specifies which Finder Flag + bits to set or clear. If a bit in flagBits is set, + then the same bit in fdFlags is either set or + cleared depending on the state of the setBits + parameter. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetInvisible + +OSErr +FSSetInvisible( + const FSRef *ref); + +#pragma mark FSClearInvisible + +OSErr +FSClearInvisible( + const FSRef *ref); + +/* + The FSSetInvisible and FSClearInvisible functions set or clear the + kIsInvisible bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetNameLocked + +OSErr +FSSetNameLocked( + const FSRef *ref); + +#pragma mark FSClearNameLocked + +OSErr +FSClearNameLocked( + const FSRef *ref); + +/* + The FSSetNameLocked and FSClearNameLocked functions set or clear the + kNameLocked bit bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetIsStationery + +OSErr +FSSetIsStationery( + const FSRef *ref); + +#pragma mark FSClearIsStationery + +OSErr +FSClearIsStationery( + const FSRef *ref); + +/* + The FSSetIsStationery and FSClearIsStationery functions set or clear the + kIsStationery bit bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetHasCustomIcon + +OSErr +FSSetHasCustomIcon( + const FSRef *ref); + +#pragma mark FSClearHasCustomIcon + +OSErr +FSClearHasCustomIcon( + const FSRef *ref); + +/* + The FSSetHasCustomIcon and FSClearHasCustomIcon functions set or clear the + kHasCustomIcon bit bit in the finderFlags field of the specified file or + directory's finder information. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSClearHasBeenInited + +OSErr +FSClearHasBeenInited( + const FSRef *ref); + +/* + The FSClearHasBeenInited function clears the kHasBeenInited bit in the + finderFlags field of the specified file or directory's finder information. + + Note: There is no FSSetHasBeenInited function because ONLY the Finder + should set the kHasBeenInited bit. + + ref --> FSRef to a file or directory. +*/ + +/*****************************************************************************/ + +#pragma mark FSCopyFileMgrAttributes + +OSErr +FSCopyFileMgrAttributes( + const FSRef *sourceRef, + const FSRef *destinationRef, + Boolean copyLockBit); + +/* + The CopyFileMgrAttributes function copies all File Manager attributes + from the source file or directory to the destination file or directory. + If copyLockBit is true, then set the locked state of the destination + to match the source. + + sourceRef --> FSRef to a file or directory. + destinationRef --> FSRef to a file or directory. + copyLockBit --> If true, set the locked state of the destination + to match the source. +*/ + +/*****************************************************************************/ + +#pragma mark FSMoveRenameObjectUnicode + +OSErr +FSMoveRenameObjectUnicode( + const FSRef *ref, + const FSRef *destDirectory, + UniCharCount nameLength, + const UniChar *name, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef); /* if function fails along the way, newRef is final location of file */ + +/* + The FSMoveRenameObjectUnicode function moves a file or directory and + optionally renames it. The source and destination locations must be on + the same volume. + + Note: If the input ref parameter is invalid, this call will fail and + newRef, like ref, will be invalid. + + ref --> FSRef to a file or directory. + destDirectory --> FSRef to the destination directory. + nameLength --> Number of UniChar in name parameter. + name --> An Unicode string with the new name for the + moved object, or NULL if no rename is wanted. + textEncodingHint --> The text encoding hint used for the rename. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + newRef <-- The new FSRef of the object moved. Note that if + this function fails at any step along the way, + newRef is still then final location of the object. +*/ + +/*****************************************************************************/ + +#pragma mark FSDeleteContainerContents + +OSErr +FSDeleteContainerContents( + const FSRef *container); + +/* + The FSDeleteContainerContents function deletes the contents of a container + directory. All files and subdirectories in the specified container are + deleted. If a locked file or directory is encountered, it is unlocked and + then deleted. If any unexpected errors are encountered, + FSDeleteContainerContents quits and returns to the caller. + + container --> FSRef to a directory. + + __________ + + Also see: FSDeleteContainer +*/ + +/*****************************************************************************/ + +#pragma mark FSDeleteContainer + +OSErr +FSDeleteContainer( + const FSRef *container); + +/* + The FSDeleteContainer function deletes a container directory and its contents. + All files and subdirectories in the specified container are deleted. + If a locked file or directory is encountered, it is unlocked and then + deleted. After deleting the container's contents, the container is + deleted. If any unexpected errors are encountered, FSDeleteContainer + quits and returns to the caller. + + container --> FSRef to a directory. + + __________ + + Also see: FSDeleteContainerContents +*/ + +/*****************************************************************************/ + +#pragma mark IterateContainerFilterProcPtr + +typedef CALLBACK_API( Boolean , IterateContainerFilterProcPtr ) ( + Boolean containerChanged, + ItemCount currentLevel, + const FSCatalogInfo *catalogInfo, + const FSRef *ref, + const FSSpec *spec, + const HFSUniStr255 *name, + void *yourDataPtr); + +/* + This is the prototype for the IterateContainerFilterProc function which + is called once for each file and directory found by FSIterateContainer. + The IterateContainerFilterProc can use the read-only data it receives for + whatever it wants. + + The result of the IterateContainerFilterProc function indicates if + iteration should be stopped. To stop iteration, return true; to continue + iteration, return false. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within the IterateContainerFilterProc. + + containerChanged --> Set to true if the container's contents changed + during iteration. + currentLevel --> The current recursion level into the container. + 1 = the container, 2 = the container's immediate + subdirectories, etc. + catalogInfo --> The catalog information for the current object. + Only the fields requested by the whichInfo + parameter passed to FSIterateContainer are valid. + ref --> The FSRef to the current object. + spec --> The FSSpec to the current object if the wantFSSpec + parameter passed to FSIterateContainer is true. + name --> The name of the current object if the wantName + parameter passed to FSIterateContainer is true. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + IterateFilterProc. + result <-- To stop iteration, return true; to continue + iteration, return false. + + __________ + + Also see: FSIterateContainer +*/ + +/*****************************************************************************/ + +#pragma mark CallIterateContainerFilterProc + +#define CallIterateContainerFilterProc(userRoutine, containerChanged, currentLevel, catalogInfo, ref, spec, name, yourDataPtr) \ + (*(userRoutine))((containerChanged), (currentLevel), (catalogInfo), (ref), (spec), (name), (yourDataPtr)) + +/*****************************************************************************/ + +#pragma mark FSIterateContainer + +OSErr +FSIterateContainer( + const FSRef *container, + ItemCount maxLevels, + FSCatalogInfoBitmap whichInfo, + Boolean wantFSSpec, + Boolean wantName, + IterateContainerFilterProcPtr iterateFilter, + void *yourDataPtr); + +/* + The FSIterateContainer function performs a recursive iteration (scan) of the + specified container directory and calls your IterateContainerFilterProc + function once for each file and directory found. + + The maxLevels parameter lets you control how deep the recursion goes. + If maxLevels is 1, FSIterateContainer only scans the specified directory; + if maxLevels is 2, FSIterateContainer scans the specified directory and + one subdirectory below the specified directory; etc. Set maxLevels to + zero to scan all levels. + + The yourDataPtr parameter can point to whatever data structure you might + want to access from within your IterateContainerFilterProc. + + container --> The FSRef to the container directory to iterate. + maxLevels --> Maximum number of directory levels to scan or + zero to scan all directory levels. + whichInfo --> The fields of the FSCatalogInfo you wish to get. + wantFSSpec --> Set to true if you want the FSSpec to each + object passed to your IterateContainerFilterProc. + wantName --> Set to true if you want the name of each + object passed to your IterateContainerFilterProc. + iterateFilter --> A pointer to the IterateContainerFilterProc you + want called once for each file and directory found + by FSIterateContainer. + yourDataPtr --> An optional pointer to whatever data structure you + might want to access from within the + IterateFilterProc. +*/ + +/*****************************************************************************/ + +#pragma mark FSGetDirectoryItems + +OSErr +FSGetDirectoryItems( + const FSRef *container, + FSRef ***refsHandle, /* pointer to handle of FSRefs */ + ItemCount *numRefs, + Boolean *containerChanged); + +/* + The FSGetDirectoryItems function returns the list of items in the specified + container. The array of FSRef records is returned in a Handle, refsHandle, + which is allocated by FSGetDirectoryItems. The caller is responsible for + disposing of refsHandle if the FSGetDirectoryItems returns noErr. + + container --> FSRef to a directory. + refsHandle <-- Pointer to an FSRef Handle where the array of + FSRefs is to be returned. + numRefs <-- The number of FSRefs returned in the array. + containerChanged <-- Set to true if the container changes while the + list of items is being obtained. +*/ + +/*****************************************************************************/ + +#pragma mark FSExchangeObjectsCompat + +OSErr +FSExchangeObjectsCompat( + const FSRef *sourceRef, + const FSRef *destRef, + FSRef *newSourceRef, + FSRef *newDestRef); + +/* + The FSExchangeObjectsCompat function exchanges the data between two files. + + The FSExchangeObjectsCompat function is an enhanced version of + FSExchangeObjects function. The two enhancements FSExchangeObjectsCompat + provides are: + + 1, FSExchangeObjectsCompat will work on volumes which do not support + FSExchangeObjects. FSExchangeObjectsCompat does this by emulating + FSExchangeObjects through a series of File Manager operations. If + there is a failure at any step along the way, FSExchangeObjectsCompat + attempts to undo any steps already taken to leave the files in their + original state in their original locations. + + 2. FSExchangeObjectsCompat returns new FSRefs to the source and + destination files. Note that if this function fails at any step along + the way, newSourceRef and newDestRef still give you access to the final + locations of the files being exchanged -- even if they are renamed or + not in their original locations. + + sourceRef --> FSRef to the source file. + destRef --> FSRef to the destination file. + newSourceRef <-- The new FSRef to the source file. + newDestRef <-- The new FSRef to the destination file. +*/ + +/*****************************************************************************/ + +#pragma mark ----- Shared Environment Routines ----- + +/*****************************************************************************/ + +#pragma mark FSLockRange + +OSErr +FSLockRangeMoreFilesX( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart); + +/* + The LockRange function locks (denies access to) a portion of a file + that was opened with shared read/write permission. + + refNum --> The file reference number of an open file. + rangeLength --> The number of bytes in the range. + rangeStart --> The starting byte in the range to lock. + + __________ + + Also see: UnlockRange +*/ + +/*****************************************************************************/ + +#pragma mark FSUnlockRange + +OSErr +FSUnlockRangeMoreFilesX( + SInt16 refNum, + SInt32 rangeLength, + SInt32 rangeStart); + +/* + The UnlockRange function unlocks (allows access to) a previously locked + portion of a file that was opened with shared read/write permission. + + refNum --> The file reference number of an open file. + rangeLength --> The number of bytes in the range. + rangeStart --> The starting byte in the range to unlock. + + __________ + + Also see: LockRange +*/ + +/*****************************************************************************/ + +#pragma mark FSGetDirAccess + +OSErr +FSGetDirAccess( + const FSRef *ref, + SInt32 *ownerID, /* can be NULL */ + SInt32 *groupID, /* can be NULL */ + SInt32 *accessRights); /* can be NULL */ + +/* + The FSGetDirAccess function retrieves the directory access control + information for a directory on a shared volume. + + ref --> An FSRef specifying the directory. + ownerID <** An optional pointer to a SInt32. + If not NULL, the directory's owner ID + will be returned in the SInt32. + groupID <** An optional pointer to a SInt32. + If not NULL, the directory's group ID, or 0 + if no group affiliation, will be returned in + the SInt32. + accessRights <** An optional pointer to a SInt32. + If not NULL, the directory's access rights + will be returned in the SInt32. + + __________ + + Also see: FSSetDirAccess, FSMapID, FSMapName +*/ + +/*****************************************************************************/ + +#pragma mark FSSetDirAccess + +OSErr +FSSetDirAccess( + const FSRef *ref, + SInt32 ownerID, + SInt32 groupID, + SInt32 accessRights); + +/* + The FSpSetDirAccess function changes the directory access control + information for a directory on a shared volume. You must be the owner of + a directory to change its access control information. + + ref --> An FSRef specifying the directory. + ownerID --> The directory's owner ID. + groupID --> The directory's group ID or 0 if no group affiliation. + accessRights --> The directory's access rights. + + __________ + + Also see: FSGetDirAccess, FSMapID, FSMapName +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVolMountInfoSize + +OSErr +FSGetVolMountInfoSize( + FSVolumeRefNum volRefNum, + SInt16 *size); + +/* + The FSGetVolMountInfoSize function determines the how much space the + program needs to allocate for a volume mounting information record. + + volRefNum --> Volume specification. + size <-- The space needed (in bytes) of the volume + mounting information record. + + __________ + + Also see: FSGetVolMountInfo, VolumeMount +*/ + +/*****************************************************************************/ + +#pragma mark FSGetVolMountInfo + +OSErr +FSGetVolMountInfo( + FSVolumeRefNum volRefNum, + void *volMountInfo); + +/* + The FSGetVolMountInfo function retrieves a volume mounting information + record containing all the information needed to mount the volume, + except for passwords. + + volRefNum --> Volume specification. + volMountInfo <-- The volume mounting information. + + __________ + + Also see: FSGetVolMountInfoSize, VolumeMount +*/ + +/*****************************************************************************/ + +#pragma mark FSVolumeMount + +OSErr +FSVolumeMountX( + const void *volMountInfo, + FSVolumeRefNum *volRefNum); + +/* + The VolumeMount function mounts a volume using a volume mounting + information record. + + volMountInfo --> A volume mounting information record. + volRefNum <-- The volume reference number. + + __________ + + Also see: FSGetVolMountInfoSize, FSGetVolMountInfo +*/ + +/*****************************************************************************/ + +#pragma mark FSMapID + +OSErr +FSMapID( + FSVolumeRefNum volRefNum, + SInt32 ugID, + SInt16 objType, + Str31 name); + +/* + The FSMapID function determines the name of a user or group if you know + the user or group ID. + + volRefNum --> Volume specification. + objType --> The mapping function code: + kOwnerID2Name to map a user ID to a user name + kGroupID2Name to map a group ID to a group name + name <** An optional pointer to a buffer (minimum Str31). + If not NULL, the user or group name + will be returned in the buffer. + + __________ + + Also see: FSGetDirAccess, FSSetDirAccess, FSMapName +*/ + +/*****************************************************************************/ + +#pragma mark FSMapName + +OSErr +FSMapName( + FSVolumeRefNum volRefNum, + ConstStr255Param name, + SInt16 objType, + SInt32 *ugID); + +/* + The FSMapName function determines the user or group ID if you know the + user or group name. + + volRefNum --> Volume specification. + name --> The user or group name. + objType --> The mapping function code: + kOwnerName2ID to map a user name to a user ID + kGroupName2ID to map a user name to a group ID + ugID <-- The user or group ID. + + __________ + + Also see: FSGetDirAccess, FSSetDirAccess, FSMapID +*/ + +/*****************************************************************************/ + +#pragma mark FSCopyFile + +OSErr +FSCopyFile( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *copyName, /* can be NULL (no rename during copy) */ + TextEncoding textEncodingHint, + FSRef *newRef); /* can be NULL */ + +/* + The FSCopyFile function duplicates a file and optionally renames it. + The source and destination volumes must be on the same file server. + This function instructs the server to copy the file. + + srcFileRef --> An FSRef specifying the source file. + dstDirectoryRef --> An FSRef specifying the destination directory. + nameLength --> Number of UniChar in copyName parameter (ignored + if copyName is NULL). + copyName --> Points to the new file name if the file is to be + renamed, or NULL if the file isn't to be renamed. + textEncodingHint --> The text encoding hint used for the rename. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + newRef <** An optional pointer to a FSRef. + If not NULL, the FSRef of the duplicated file + will be returned in the FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark FSMoveRename + +OSErr +FSMoveRename( + const FSRef *srcFileRef, + const FSRef *dstDirectoryRef, + UniCharCount nameLength, + const UniChar *moveName, /* can be NULL (no rename during move) */ + TextEncoding textEncodingHint, + FSRef *newRef); /* can be NULL */ + +/* + The FSMoveRename function moves a file or directory (object), and + optionally renames it. The source and destination locations must be on + the same shared volume. + + srcFileRef --> An FSRef specifying the source file. + dstDirectoryRef --> An FSRef specifying the destination directory. + nameLength --> Number of UniChar in moveName parameter (ignored + if copyName is NULL) + moveName --> Points to the new object name if the object is to be + renamed, or NULL if the object isn't to be renamed. + textEncodingHint --> The text encoding hint used for the rename. + You can pass kTextEncodingUnknown to use the + "default" textEncodingHint. + newRef <** An optional pointer to a FSRef. + If not NULL, the FSRef of the moved object + will be returned in the FSRef. +*/ + +/*****************************************************************************/ + +#pragma mark ----- File ID Routines ----- + +/*****************************************************************************/ + +#pragma mark FSResolveFileIDRef + +OSErr +FSResolveFileIDRef( + FSVolumeRefNum volRefNum, + SInt32 fileID, + FSRef *ref); + +/* + The FSResolveFileIDRef function returns an FSRef for the file with the + specified file ID reference. + + volRefNum --> Volume specification. + fileID --> The file ID reference. + ref <-- The FSRef for the file ID reference. + + __________ + + Also see: FSCreateFileIDRef, FSDeleteFileIDRef +*/ + +/*****************************************************************************/ + +#pragma mark FSCreateFileIDRef + +OSErr +FSCreateFileIDRef( + const FSRef *ref, + SInt32 *fileID); + +/* + The FSCreateFileIDRef function creates a file ID reference for the + specified file, or if a file ID reference already exists, supplies + the file ID reference and returns the result code fidExists or afpIDExists. + + ref --> The FSRef for the file. + fileID <-- The file ID reference (if result is noErr, + fidExists, or afpIDExists). + + __________ + + Also see: GetFSRefFromFileIDRef, FSDeleteFileIDRef +*/ + +/*****************************************************************************/ + +#pragma mark FSDeleteFileIDRef + +/* + Why is there no FSDeleteFileIDRef routine? There are two reasons: + + 1. Since Mac OS 8.1, PBDeleteFileIDRef hasn't deleted file ID references. + On HFS volumes, deleting a file ID reference breaks aliases (which + use file ID references to track files as they are moved around on a + volume) and file ID references are automatically deleted when the file + they refer to is deleted. On HFS Plus volumes, file ID references are + always created when a file is created, deleted when the file is deleted, + and cannot be deleted at any other time. + + 2. PBDeleteFileIDRef causes a memory access fault under Mac OS X 10.0 + through 10.1.x. While this will be fixed in a future release, the + implementation, like the Mac OS 8/9 implementation, does not delete + file ID references. + + __________ + + Also see: GetFSRefFromFileIDRef, FSCreateFileIDRef +*/ + +/*****************************************************************************/ + +#pragma mark ----- Utility Routines ----- + +/*****************************************************************************/ + +#pragma mark GetTempBuffer + +Ptr +GetTempBuffer( + ByteCount buffReqSize, + ByteCount *buffActSize); + +/* + The GetTempBuffer function allocates a temporary buffer for file system + operations which is at least 4K bytes and a multiple of 4K bytes. + + buffReqSize --> Size you'd like the buffer to be. + buffActSize <-- The size of the buffer allocated. + function result <-- Pointer to memory allocated, or NULL if no memory + was available. The caller is responsible for + disposing of this buffer with DisposePtr. +*/ + +/*****************************************************************************/ + +#pragma mark FileRefNumGetFSRef + +OSErr +FileRefNumGetFSRef( + short refNum, + FSRef *ref); + +/* + The FileRefNumGetFSRef function gets the FSRef of an open file. + + refNum --> The file reference number of an open file. + ref <-- The FSRef to the open file. +*/ + +/*****************************************************************************/ + +#pragma mark FSSetDefault + +OSErr +FSSetDefault( + const FSRef *newDefault, + FSRef *oldDefault); + +/* + The FSSetDefault function sets the current working directory to the + directory specified by newDefault. The previous current working directory + is returned in oldDefault and must be used to restore the current working + directory to its previous state with the FSRestoreDefault function. + These two functions are designed to be used as a wrapper around + Standard I/O routines where the location of the file is implied to be the + current working directory. This is how you should use these functions: + + result = FSSetDefault(&newDefault, &oldDefault); + if ( noErr == result ) + { + // call the Stdio functions like remove, rename, + // fopen, freopen, etc here! + + result = FSRestoreDefault(&oldDefault); + } + + newDefault --> An FSRef that specifies the new current working + directory. + oldDefault <-- The previous current working directory's FSRef. + + __________ + + Also see: FSRestoreDefault +*/ + +/*****************************************************************************/ + +#pragma mark FSRestoreDefault + +OSErr +FSRestoreDefault( + const FSRef *oldDefault); + +/* + The FSRestoreDefault function restores the current working directory + to the directory specified by oldDefault. The oldDefault parameter was + previously obtained from the FSSetDefault function. + These two functions are designed to be used as a wrapper around + Standard I/O routines where the location of the file is implied to be the + current working directory. This is how you should use these functions: + + result = FSSetDefault(&newDefault, &oldDefault); + if ( noErr == result ) + { + // call the Stdio functions like remove, rename, + // fopen, freopen, etc here! + + result = FSRestoreDefault(&oldDefault); + } + + oldDefault --> The FSRef of the location to restore. + + __________ + + Also see: FSSetDefault +*/ + +/*****************************************************************************/ + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#elif PRAGMA_STRUCT_PACKPUSH + #pragma pack(pop) +#elif PRAGMA_STRUCT_PACK + #pragma pack() +#endif + +#ifdef PRAGMA_IMPORT_OFF +#pragma import off +#elif PRAGMA_IMPORT +#pragma import reset +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __MOREFILESX__ */ + diff --git a/src/osx/carbon/morefilex/MoreFilesXReadMe.txt b/src/osx/carbon/morefilex/MoreFilesXReadMe.txt new file mode 100644 index 0000000000..8f1c21db32 --- /dev/null +++ b/src/osx/carbon/morefilex/MoreFilesXReadMe.txt @@ -0,0 +1,125 @@ +MoreFilesX + +Copyright (c) 1992-2002 Apple Computer, Inc. +All rights reserved. + +________________________________________________________________________________ + +About MoreFilesX + +MoreFilesX is a collection of useful high-level File Manager routines that use the HFS Plus APIs introduced in Mac OS 9.0 wherever possible. + +While many of the routines in MoreFilesX are based on the older MoreFiles sample code (which used the older File Manager APIs), by using the HFS Plus APIs, the routines in MoreFilesX have several advantages over the older MoreFiles code: + +¥ The routines are simpler to understand because the high-level HFS Plus APIs are more powerful. + +¥ The routines support the features of the HFS Plus volume format such as long Unicode filenames and files larger than 2GB. + +¥ In many cases, the routines execute more efficiently than code that uses the older File Manager APIs -- especially on non-HFS volumes. + +¥ The routines use Apple's standard exception and assertion macros (the require, check, and verify macros) which improves readability and error handling, and which provides easy debug builds -- just add #define DEBUG 1 and every exception causes an assertion. + +¥ The routines are thread safe. There are no global or static variables so multiple program threads can use MoreFilesX routines safely. + +If you are writing new Carbon applications for Mac OS X that call the File Manager, you should use MoreFilesX -- not MoreFiles. If you're porting existing applications to Mac OS X and those applications use routines from MoreFiles, you should consider switching to the routines in MoreFilesX. + +The routines were designed for applications running in the Mac OS X Carbon environment. All of the routines will work under Mac OS 9 if you define BuildingMoreFilesXForMacOS9 to 1. Doing that removes code that calls Mac OS X only APIs. MoreFilesX cannot be used in pre-Mac OS 9 system releases. + +The routines in MoreFilesX have been tested (but not stress-tested) and are fully documented. + +________________________________________________________________________________ + +Files in the MoreFilesX Package + +MoreFilesX.c - the source code for MoreFilesX. + +MoreFilesX.h - the header files and complete documentation for the routines included in MoreFilesX. + +________________________________________________________________________________ + +How to use MoreFilesX + +You can compile the code and link it into your programs. You can cut and paste portions of it into your programs. You can use it as an example. Since MoreFilesX is sample code, many routines are there simply to show you how to use the File Manager APIs. If a routine does more or less than what you want, you can have the source so you can modify it to do exactly you want it to do. Feel free to rip MoreFilesX off and modify its code in whatever ways you find work best for you. + +You'll also notice that all routines that make other File Manager calls return an OSErr or OSStatus result. I always check for error results and you should too. + +________________________________________________________________________________ + +Documentation + +The documentation for the routines can be found in the header files. There, you'll find function prototypes, and a description of each call that includes a complete listing of all input and output parameters. For example, here's the function prototype and documentation for one of the routines, FSPathMakeFSSpec. + +OSStatus +FSPathMakeFSSpec( + UInt8 *path, + FSSpec *spec, + Boolean *isDirectory); /* can be NULL */ + +/* + The FSPathMakeFSSpec function converts a pathname to an FSSpec. + + path --> A pointer to a C String that is the pathname. The + format of the pathname you must supply can be + determined with the Gestalt selector gestaltFSAttr's + gestaltFSUsesPOSIXPathsForConversion bit. + If the gestaltFSUsesPOSIXPathsForConversion bit is + clear, the pathname must be a Mac OS File Manager + full pathname in a C string. If the + gestaltFSUsesPOSIXPathsForConversion bit is set, + the pathname must be a UTF8 encoded POSIX absolute + pathname in a C string. In either case, the pathname + returned by FSMakePath can be passed to + FSPathMakeFSSpec. + spec <-- The FSSpec. + isDirectory <** An optional pointer to a Boolean. + If not NULL, true will be returned in the Boolean + if the specified path is a directory or false will + be returned in the Boolean if the specified path is + a file. +*/ + +What do those arrows in the documentation for each routine mean? + + --> The parameter is an input + + <-- The parameter is an output. The pointer to the variable + where the output will be returned (must not be NULL). + + <** The parameter is an optional output. If it is not a + NULL pointer, it points to the variable where the output + will be returned. If it is a NULL pointer, the output will + not be returned and will possibly let the routine and the + File Manager do less work. If you don't need an optional output, + don't ask for it. + **> The parameter is an optional input. If it is not a + NULL pointer, it points to the variable containing the + input data. If it is a NULL pointer, the input is not used + and will possibly let the routine and the File Manager + do less work. + +While most of the routines in MoreFilesX have plenty of comments to clarify what the code is doing, a few have very few comments in their code because they simply make a single File Manager call. For those routines, the routine description is the comment. If something isn't clear, take a look at File Manager documentation online at . + +The methodology behind Apple's standard exception and assertion macros is clearly explained in Sean Parent's article "Living in an Exceptional World" develop, The Apple Technical Journal, August 1992 . Don't let the fact that this article is 10 years old fool you -- this is highly recommended reading. + +________________________________________________________________________________ + +Release Notes + +v1.0 Jan 25, 2002 +First Release. + +v1.0.1 Aug 23, 2002 +[2850624] Fixed C++ compile errors and Project Builder warnings. +[2853901] Updated standard disclaimer. +[2853905] Fixed #if test around header includes. +[3016251] Changed FSMoveRenameObjectUnicode to not use the Temporary folder because it isn't available on NFS volumes. + +________________________________________________________________________________ + +Bug Reports and Enhancement Requests + +To file bug reports and enhancement requests, please go to and include "MoreFilesX Sample Code" in the title of your message. + +Yes, we know that some of the routines available in MoreFiles still aren't in MoreFilesX. They were omitted due to time constraints. + +________________________________________________________________________________ diff --git a/src/osx/carbon/msgdlg.cpp b/src/osx/carbon/msgdlg.cpp new file mode 100644 index 0000000000..830efb6f27 --- /dev/null +++ b/src/osx/carbon/msgdlg.cpp @@ -0,0 +1,284 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/msgdlg.cpp +// Purpose: wxMessageDialog +// Author: Stefan Csomor +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/msgdlg.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/app.h" +#endif + +#include "wx/thread.h" +#include "wx/mac/uma.h" + + +IMPLEMENT_CLASS(wxMessageDialog, wxDialog) + + +wxMessageDialog::wxMessageDialog(wxWindow *parent, + const wxString& message, + const wxString& caption, + long style, + const wxPoint& WXUNUSED(pos)) + : wxMessageDialogBase(parent, message, caption, style) +{ + m_yes = _("Yes"); + m_no = _("No"); + m_ok = _("OK"); + m_cancel = _("Cancel"); +} + +bool wxMessageDialog::SetYesNoLabels(const wxString& yes,const wxString& no) +{ + m_yes = yes; + m_no = no; + return true; +} + +bool wxMessageDialog::SetYesNoCancelLabels(const wxString& yes, const wxString& no, const wxString& cancel) +{ + m_yes = yes; + m_no = no; + m_cancel = cancel; + return true; +} + +bool wxMessageDialog::SetOKLabel(const wxString& ok) +{ + m_ok = ok; + return true; +} + +bool wxMessageDialog::SetOKCancelLabels(const wxString& ok, const wxString& cancel) +{ + m_ok = ok; + m_cancel = cancel; + return true; +} + +int wxMessageDialog::ShowModal() +{ + int resultbutton = wxID_CANCEL; + + const long style = GetMessageDialogStyle(); + + wxASSERT_MSG( (style & 0x3F) != wxYES, wxT("this style is not supported on Mac") ); + + AlertType alertType = kAlertPlainAlert; + if (style & wxICON_EXCLAMATION) + alertType = kAlertCautionAlert; + else if (style & wxICON_HAND) + alertType = kAlertStopAlert; + else if (style & wxICON_INFORMATION) + alertType = kAlertNoteAlert; + else if (style & wxICON_QUESTION) + alertType = kAlertNoteAlert; + + + // work out what to display + // if the extended text is empty then we use the caption as the title + // and the message as the text (for backwards compatibility) + // but if the extended message is not empty then we use the message as the title + // and the extended message as the text because that makes more sense + + wxString msgtitle,msgtext; + if(m_extendedMessage.IsEmpty()) + { + msgtitle = m_caption; + msgtext = m_message; + } + else + { + msgtitle = m_message; + msgtext = m_extendedMessage; + } + + + if ( !wxIsMainThread() ) + { + CFStringRef defaultButtonTitle = NULL; + CFStringRef alternateButtonTitle = NULL; + CFStringRef otherButtonTitle = NULL; + + wxCFStringRef cfTitle( msgtitle, GetFont().GetEncoding() ); + wxCFStringRef cfText( msgtext, GetFont().GetEncoding() ); + + wxCFStringRef cfNoString( m_no.c_str(), GetFont().GetEncoding() ); + wxCFStringRef cfYesString( m_yes.c_str(), GetFont().GetEncoding() ); + wxCFStringRef cfOKString( m_ok.c_str() , GetFont().GetEncoding()) ; + wxCFStringRef cfCancelString( m_cancel.c_str(), GetFont().GetEncoding() ); + + int buttonId[4] = { 0, 0, 0, wxID_CANCEL /* time-out */ }; + + if (style & wxYES_NO) + { + if ( style & wxNO_DEFAULT ) + { + defaultButtonTitle = cfNoString; + alternateButtonTitle = cfYesString; + buttonId[0] = wxID_NO; + buttonId[1] = wxID_YES; + } + else + { + defaultButtonTitle = cfYesString; + alternateButtonTitle = cfNoString; + buttonId[0] = wxID_YES; + buttonId[1] = wxID_NO; + } + if (style & wxCANCEL) + { + otherButtonTitle = cfCancelString; + buttonId[2] = wxID_CANCEL; + } + } + else + { + // the MSW implementation even shows an OK button if it is not specified, we'll do the same + buttonId[0] = wxID_OK; + // using null as default title does not work on earlier systems + defaultButtonTitle = cfOKString; + if (style & wxCANCEL) + { + alternateButtonTitle = cfCancelString; + buttonId[1] = wxID_CANCEL; + } + } + + CFOptionFlags exitButton; + OSStatus err = CFUserNotificationDisplayAlert( + 0, alertType, NULL, NULL, NULL, cfTitle, cfText, + defaultButtonTitle, alternateButtonTitle, otherButtonTitle, &exitButton ); + if (err == noErr) + resultbutton = buttonId[exitButton]; + } + else + { + short result; + + AlertStdCFStringAlertParamRec param; + wxCFStringRef cfNoString( m_no.c_str(), GetFont().GetEncoding() ); + wxCFStringRef cfYesString( m_yes.c_str(), GetFont().GetEncoding() ); + wxCFStringRef cfOKString( m_ok.c_str(), GetFont().GetEncoding() ); + wxCFStringRef cfCancelString( m_cancel.c_str(), GetFont().GetEncoding() ); + + wxCFStringRef cfTitle( msgtitle, GetFont().GetEncoding() ); + wxCFStringRef cfText( msgtext, GetFont().GetEncoding() ); + + param.movable = true; + param.flags = 0; + param.version = kStdCFStringAlertVersionOne; + + bool skipDialog = false; + + if (style & wxYES_NO) + { + if (style & wxCANCEL) + { + param.defaultText = cfYesString; + param.cancelText = cfCancelString; + param.otherText = cfNoString; + param.helpButton = false; + param.defaultButton = style & wxNO_DEFAULT ? kAlertStdAlertOtherButton : kAlertStdAlertOKButton; + param.cancelButton = kAlertStdAlertCancelButton; + } + else + { + param.defaultText = cfYesString; + param.cancelText = NULL; + param.otherText = cfNoString; + param.helpButton = false; + param.defaultButton = style & wxNO_DEFAULT ? kAlertStdAlertOtherButton : kAlertStdAlertOKButton; + param.cancelButton = 0; + } + } + // the MSW implementation even shows an OK button if it is not specified, we'll do the same + else + { + if (style & wxCANCEL) + { + // that's a cancel missing + param.defaultText = cfOKString; + param.cancelText = cfCancelString; + param.otherText = NULL; + param.helpButton = false; + param.defaultButton = kAlertStdAlertOKButton; + param.cancelButton = 0; + } + else + { + param.defaultText = cfOKString; + param.cancelText = NULL; + param.otherText = NULL; + param.helpButton = false; + param.defaultButton = kAlertStdAlertOKButton; + param.cancelButton = 0; + } + } + + param.position = kWindowDefaultPosition; + if ( !skipDialog ) + { + DialogRef alertRef; + CreateStandardAlert( alertType, cfTitle, cfText, ¶m, &alertRef ); + RunStandardAlert( alertRef, NULL, &result ); + } + else + { + return wxID_CANCEL; + } + + if (style & wxOK) + { + switch ( result ) + { + case 1: + resultbutton = wxID_OK; + break; + + case 2: + // TODO: add Cancel button + // if (style & wxCANCEL) + // resultbutton = wxID_CANCEL; + break; + + case 3: + default: + break; + } + } + else if (style & wxYES_NO) + { + switch ( result ) + { + case 1: + resultbutton = wxID_YES; + break; + + case 2: + if (!(style & wxCANCEL)) + resultbutton = wxID_CANCEL; + break; + + case 3: + resultbutton = wxID_NO; + break; + + default: + break; + } + } + } + + return resultbutton; +} diff --git a/src/osx/carbon/nonownedwnd.cpp b/src/osx/carbon/nonownedwnd.cpp new file mode 100644 index 0000000000..4c8d95e72e --- /dev/null +++ b/src/osx/carbon/nonownedwnd.cpp @@ -0,0 +1,1689 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/nonownedwnd.cpp +// Purpose: implementation of wxNonOwnedWindow +// Author: Stefan Csomor +// Created: 2008-03-24 +// RCS-ID: $Id: nonownedwnd.cpp 50329 2007-11-29 17:00:58Z VS $ +// Copyright: (c) Stefan Csomor 2008 +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" +#endif // WX_PRECOMP + +#include "wx/hashmap.h" +#include "wx/evtloop.h" +#include "wx/tooltip.h" +#include "wx/nonownedwnd.h" + +#include "wx/mac/private.h" +#include "wx/settings.h" +#include "wx/frame.h" + +#if wxUSE_SYSTEM_OPTIONS + #include "wx/sysopt.h" +#endif + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// unified title and toolbar constant - not in Tiger headers, so we duplicate it here +#define kWindowUnifiedTitleAndToolbarAttribute (1 << 7) + +// trace mask for activation tracing messages +#define TRACE_ACTIVATE "activation" + +// ---------------------------------------------------------------------------- +// globals +// ---------------------------------------------------------------------------- + +static pascal long wxShapedMacWindowDef(short varCode, WindowRef window, SInt16 message, SInt32 param); + +// ============================================================================ +// wxNonOwnedWindow implementation +// ============================================================================ + +// --------------------------------------------------------------------------- +// Carbon Events +// --------------------------------------------------------------------------- + +static const EventTypeSpec eventList[] = +{ + // TODO: remove control related event like key and mouse (except for WindowLeave events) + + { kEventClassKeyboard, kEventRawKeyDown } , + { kEventClassKeyboard, kEventRawKeyRepeat } , + { kEventClassKeyboard, kEventRawKeyUp } , + { kEventClassKeyboard, kEventRawKeyModifiersChanged } , + + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } , + { kEventClassTextInput, kEventTextInputUpdateActiveInputArea } , + + { kEventClassWindow , kEventWindowShown } , + { kEventClassWindow , kEventWindowActivated } , + { kEventClassWindow , kEventWindowDeactivated } , + { kEventClassWindow , kEventWindowBoundsChanging } , + { kEventClassWindow , kEventWindowBoundsChanged } , + { kEventClassWindow , kEventWindowClose } , + { kEventClassWindow , kEventWindowGetRegion } , + + // we have to catch these events on the toplevel window level, + // as controls don't get the raw mouse events anymore + + { kEventClassMouse , kEventMouseDown } , + { kEventClassMouse , kEventMouseUp } , + { kEventClassMouse , kEventMouseWheelMoved } , + { kEventClassMouse , kEventMouseMoved } , + { kEventClassMouse , kEventMouseDragged } , +} ; + +static pascal OSStatus KeyboardEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + // call DoFindFocus instead of FindFocus, because for Composite Windows(like WxGenericListCtrl) + // FindFocus does not return the actual focus window, but the enclosing window + wxWindow* focus = wxWindow::DoFindFocus(); + if ( focus == NULL ) + focus = (wxNonOwnedWindow*) data ; + + unsigned char charCode ; + wxChar uniChar[2] ; + uniChar[0] = 0; + uniChar[1] = 0; + + UInt32 keyCode ; + UInt32 modifiers ; + Point point ; + UInt32 when = EventTimeToTicks( GetEventTime( event ) ) ; + +#if wxUSE_UNICODE + ByteCount dataSize = 0 ; + if ( GetEventParameter( event, kEventParamKeyUnicodes, typeUnicodeText, NULL, 0 , &dataSize, NULL ) == noErr ) + { + UniChar buf[2] ; + int numChars = dataSize / sizeof( UniChar) + 1; + + UniChar* charBuf = buf ; + + if ( numChars * 2 > 4 ) + charBuf = new UniChar[ numChars ] ; + GetEventParameter( event, kEventParamKeyUnicodes, typeUnicodeText, NULL, dataSize , NULL , charBuf ) ; + charBuf[ numChars - 1 ] = 0; + +#if SIZEOF_WCHAR_T == 2 + uniChar = charBuf[0] ; +#else + wxMBConvUTF16 converter ; + converter.MB2WC( uniChar , (const char*)charBuf , 2 ) ; +#endif + + if ( numChars * 2 > 4 ) + delete[] charBuf ; + } +#endif + + GetEventParameter( event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &charCode ); + GetEventParameter( event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode ); + GetEventParameter( event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers ); + GetEventParameter( event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &point ); + + UInt32 message = (keyCode << 8) + charCode; + switch ( GetEventKind( event ) ) + { + case kEventRawKeyRepeat : + case kEventRawKeyDown : + { + WXEVENTREF formerEvent = wxTheApp->MacGetCurrentEvent() ; + WXEVENTHANDLERCALLREF formerHandler = wxTheApp->MacGetCurrentEventHandlerCallRef() ; + wxTheApp->MacSetCurrentEvent( event , handler ) ; + if ( /* focus && */ wxTheApp->MacSendKeyDownEvent( + focus , message , modifiers , when , point.h , point.v , uniChar[0] ) ) + { + result = noErr ; + } + wxTheApp->MacSetCurrentEvent( formerEvent , formerHandler ) ; + } + break ; + + case kEventRawKeyUp : + if ( /* focus && */ wxTheApp->MacSendKeyUpEvent( + focus , message , modifiers , when , point.h , point.v , uniChar[0] ) ) + { + result = noErr ; + } + break ; + + case kEventRawKeyModifiersChanged : + { + wxKeyEvent event(wxEVT_KEY_DOWN); + + event.m_shiftDown = modifiers & shiftKey; + event.m_controlDown = modifiers & controlKey; + event.m_altDown = modifiers & optionKey; + event.m_metaDown = modifiers & cmdKey; + event.m_x = point.h; + event.m_y = point.v; + +#if wxUSE_UNICODE + event.m_uniChar = uniChar[0] ; +#endif + + event.SetTimestamp(when); + event.SetEventObject(focus); + + if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & controlKey ) + { + event.m_keyCode = WXK_CONTROL ; + event.SetEventType( ( modifiers & controlKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ; + focus->HandleWindowEvent( event ) ; + } + if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & shiftKey ) + { + event.m_keyCode = WXK_SHIFT ; + event.SetEventType( ( modifiers & shiftKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ; + focus->HandleWindowEvent( event ) ; + } + if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & optionKey ) + { + event.m_keyCode = WXK_ALT ; + event.SetEventType( ( modifiers & optionKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ; + focus->HandleWindowEvent( event ) ; + } + if ( /* focus && */ (modifiers ^ wxApp::s_lastModifiers ) & cmdKey ) + { + event.m_keyCode = WXK_COMMAND ; + event.SetEventType( ( modifiers & cmdKey ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ) ; + focus->HandleWindowEvent( event ) ; + } + + wxApp::s_lastModifiers = modifiers ; + } + break ; + + default: + break; + } + + return result ; +} + +// we don't interfere with foreign controls on our toplevel windows, therefore we always give back eventNotHandledErr +// for windows that we didn't create (like eg Scrollbars in a databrowser), or for controls where we did not handle the +// mouse down at all +// +// This handler can also be called from app level where data (ie target window) may be null or a non wx window + +wxWindow* g_MacLastWindow = NULL ; + +EventMouseButton g_lastButton = 0 ; +bool g_lastButtonWasFakeRight = false ; + +void SetupMouseEvent( wxMouseEvent &wxevent , wxMacCarbonEvent &cEvent ) +{ + UInt32 modifiers = cEvent.GetParameter(kEventParamKeyModifiers, typeUInt32) ; + Point screenMouseLocation = cEvent.GetParameter(kEventParamMouseLocation) ; + + // these parameters are not given for all events + EventMouseButton button = 0 ; + UInt32 clickCount = 0 ; + UInt32 mouseChord = 0; + + cEvent.GetParameter( kEventParamMouseButton, typeMouseButton , &button ) ; + cEvent.GetParameter( kEventParamClickCount, typeUInt32 , &clickCount ) ; + // the chord is the state of the buttons pressed currently + cEvent.GetParameter( kEventParamMouseChord, typeUInt32 , &mouseChord ) ; + + wxevent.m_x = screenMouseLocation.h; + wxevent.m_y = screenMouseLocation.v; + wxevent.m_shiftDown = modifiers & shiftKey; + wxevent.m_controlDown = modifiers & controlKey; + wxevent.m_altDown = modifiers & optionKey; + wxevent.m_metaDown = modifiers & cmdKey; + wxevent.m_clickCount = clickCount; + wxevent.SetTimestamp( cEvent.GetTicks() ) ; + + // a control click is interpreted as a right click + bool thisButtonIsFakeRight = false ; + if ( button == kEventMouseButtonPrimary && (modifiers & controlKey) ) + { + button = kEventMouseButtonSecondary ; + thisButtonIsFakeRight = true ; + } + + // otherwise we report double clicks by connecting a left click with a ctrl-left click + if ( clickCount > 1 && button != g_lastButton ) + clickCount = 1 ; + + // we must make sure that our synthetic 'right' button corresponds in + // mouse down, moved and mouse up, and does not deliver a right down and left up + + if ( cEvent.GetKind() == kEventMouseDown ) + { + g_lastButton = button ; + g_lastButtonWasFakeRight = thisButtonIsFakeRight ; + } + + if ( button == 0 ) + { + g_lastButton = 0 ; + g_lastButtonWasFakeRight = false ; + } + else if ( g_lastButton == kEventMouseButtonSecondary && g_lastButtonWasFakeRight ) + button = g_lastButton ; + + // Adjust the chord mask to remove the primary button and add the + // secondary button. It is possible that the secondary button is + // already pressed, e.g. on a mouse connected to a laptop, but this + // possibility is ignored here: + if( thisButtonIsFakeRight && ( mouseChord & 1U ) ) + mouseChord = ((mouseChord & ~1U) | 2U); + + if(mouseChord & 1U) + wxevent.m_leftDown = true ; + if(mouseChord & 2U) + wxevent.m_rightDown = true ; + if(mouseChord & 4U) + wxevent.m_middleDown = true ; + + // translate into wx types + switch ( cEvent.GetKind() ) + { + case kEventMouseDown : + switch ( button ) + { + case kEventMouseButtonPrimary : + wxevent.SetEventType( clickCount > 1 ? wxEVT_LEFT_DCLICK : wxEVT_LEFT_DOWN ) ; + break ; + + case kEventMouseButtonSecondary : + wxevent.SetEventType( clickCount > 1 ? wxEVT_RIGHT_DCLICK : wxEVT_RIGHT_DOWN ) ; + break ; + + case kEventMouseButtonTertiary : + wxevent.SetEventType( clickCount > 1 ? wxEVT_MIDDLE_DCLICK : wxEVT_MIDDLE_DOWN ) ; + break ; + + default: + break ; + } + break ; + + case kEventMouseUp : + switch ( button ) + { + case kEventMouseButtonPrimary : + wxevent.SetEventType( wxEVT_LEFT_UP ) ; + break ; + + case kEventMouseButtonSecondary : + wxevent.SetEventType( wxEVT_RIGHT_UP ) ; + break ; + + case kEventMouseButtonTertiary : + wxevent.SetEventType( wxEVT_MIDDLE_UP ) ; + break ; + + default: + break ; + } + break ; + + case kEventMouseWheelMoved : + { + wxevent.SetEventType( wxEVT_MOUSEWHEEL ) ; + + EventMouseWheelAxis axis = cEvent.GetParameter(kEventParamMouseWheelAxis, typeMouseWheelAxis) ; + SInt32 delta = cEvent.GetParameter(kEventParamMouseWheelDelta, typeSInt32) ; + + wxevent.m_wheelRotation = delta; + wxevent.m_wheelDelta = 1; + wxevent.m_linesPerAction = 1; + if ( axis == kEventMouseWheelAxisX ) + wxevent.m_wheelAxis = 1; + } + break ; + + case kEventMouseEntered : + case kEventMouseExited : + case kEventMouseDragged : + case kEventMouseMoved : + wxevent.SetEventType( wxEVT_MOTION ) ; + break; + default : + break ; + } +} + +#define NEW_CAPTURE_HANDLING 1 + +pascal OSStatus +wxMacTopLevelMouseEventHandler(EventHandlerCallRef WXUNUSED(handler), + EventRef event, + void *data) +{ + wxNonOwnedWindow* toplevelWindow = (wxNonOwnedWindow*) data ; + + OSStatus result = eventNotHandledErr ; + + wxMacCarbonEvent cEvent( event ) ; + + Point screenMouseLocation = cEvent.GetParameter(kEventParamMouseLocation) ; + Point windowMouseLocation = screenMouseLocation ; + + WindowRef window = NULL; + short windowPart = ::FindWindow(screenMouseLocation, &window); + + wxWindow* currentMouseWindow = NULL ; + ControlRef control = NULL ; + +#if NEW_CAPTURE_HANDLING + if ( wxApp::s_captureWindow ) + { + window = (WindowRef) wxApp::s_captureWindow->MacGetTopLevelWindowRef() ; + windowPart = inContent ; + } +#endif + + if ( window ) + { + wxMacGlobalToLocal( window, &windowMouseLocation ) ; + + if ( wxApp::s_captureWindow +#if !NEW_CAPTURE_HANDLING + && wxApp::s_captureWindow->MacGetTopLevelWindowRef() == (WXWindow) window && windowPart == inContent +#endif + ) + { + currentMouseWindow = wxApp::s_captureWindow ; + } + else if ( (IsWindowActive(window) && windowPart == inContent) ) + { + ControlPartCode part ; + control = FindControlUnderMouse( windowMouseLocation , window , &part ) ; + // if there is no control below the mouse position, send the event to the toplevel window itself + if ( control == 0 ) + { + currentMouseWindow = (wxWindow*) data ; + } + else + { + currentMouseWindow = (wxWindow*) wxFindControlFromMacControl( control ) ; +#ifndef __WXUNIVERSAL__ + if ( currentMouseWindow == NULL && cEvent.GetKind() == kEventMouseMoved ) + { +#if wxUSE_TOOLBAR + // for wxToolBar to function we have to send certaint events to it + // instead of its children (wxToolBarTools) + ControlRef parent ; + GetSuperControl(control, &parent ); + wxWindow *wxParent = (wxWindow*) wxFindControlFromMacControl( parent ) ; + if ( wxParent && wxParent->IsKindOf( CLASSINFO( wxToolBar ) ) ) + currentMouseWindow = wxParent ; +#endif + } +#endif + } + + // disabled windows must not get any input messages + if ( currentMouseWindow && !currentMouseWindow->MacIsReallyEnabled() ) + currentMouseWindow = NULL; + } + } + + wxMouseEvent wxevent(wxEVT_LEFT_DOWN); + SetupMouseEvent( wxevent , cEvent ) ; + + // handle all enter / leave events + + if ( currentMouseWindow != g_MacLastWindow ) + { + if ( g_MacLastWindow ) + { + wxMouseEvent eventleave(wxevent); + eventleave.SetEventType( wxEVT_LEAVE_WINDOW ); + g_MacLastWindow->ScreenToClient( &eventleave.m_x, &eventleave.m_y ); + eventleave.SetEventObject( g_MacLastWindow ) ; + wxevent.SetId( g_MacLastWindow->GetId() ) ; + +#if wxUSE_TOOLTIPS + wxToolTip::RelayEvent( g_MacLastWindow , eventleave); +#endif + + g_MacLastWindow->HandleWindowEvent(eventleave); + } + + if ( currentMouseWindow ) + { + wxMouseEvent evententer(wxevent); + evententer.SetEventType( wxEVT_ENTER_WINDOW ); + currentMouseWindow->ScreenToClient( &evententer.m_x, &evententer.m_y ); + evententer.SetEventObject( currentMouseWindow ) ; + wxevent.SetId( currentMouseWindow->GetId() ) ; + +#if wxUSE_TOOLTIPS + wxToolTip::RelayEvent( currentMouseWindow , evententer ); +#endif + + currentMouseWindow->HandleWindowEvent(evententer); + } + + g_MacLastWindow = currentMouseWindow ; + } + + if ( windowPart == inMenuBar ) + { + // special case menu bar, as we are having a low-level runloop we must do it ourselves + if ( cEvent.GetKind() == kEventMouseDown ) + { + ::MenuSelect( screenMouseLocation ) ; + ::HiliteMenu(0); + result = noErr ; + } + } + else if ( currentMouseWindow ) + { + wxWindow *currentMouseWindowParent = currentMouseWindow->GetParent(); + + currentMouseWindow->ScreenToClient( &wxevent.m_x , &wxevent.m_y ) ; + + wxevent.SetEventObject( currentMouseWindow ) ; + wxevent.SetId( currentMouseWindow->GetId() ) ; + + // make tooltips current + +#if wxUSE_TOOLTIPS + if ( wxevent.GetEventType() == wxEVT_MOTION ) + wxToolTip::RelayEvent( currentMouseWindow , wxevent ); +#endif + + if ( currentMouseWindow->HandleWindowEvent(wxevent) ) + { + if ((currentMouseWindowParent != NULL) && + (currentMouseWindowParent->GetChildren().Find(currentMouseWindow) == NULL)) + currentMouseWindow = NULL; + + result = noErr; + } + else + { + // if the user code did _not_ handle the event, then perform the + // default processing + if ( wxevent.GetEventType() == wxEVT_LEFT_DOWN ) + { + // ... that is set focus to this window + if (currentMouseWindow->CanAcceptFocus() && wxWindow::FindFocus()!=currentMouseWindow) + currentMouseWindow->SetFocus(); + } + } + + if ( cEvent.GetKind() == kEventMouseUp && wxApp::s_captureWindow ) + { + wxApp::s_captureWindow = NULL ; + // update cursor ? + } + + // update cursor + + wxWindow* cursorTarget = currentMouseWindow ; + wxPoint cursorPoint( wxevent.m_x , wxevent.m_y ) ; + + extern wxCursor gGlobalCursor; + + if (!gGlobalCursor.IsOk()) + { + while ( cursorTarget && !cursorTarget->MacSetupCursor( cursorPoint ) ) + { + cursorTarget = cursorTarget->GetParent() ; + if ( cursorTarget ) + cursorPoint += cursorTarget->GetPosition(); + } + } + + } + else // currentMouseWindow == NULL + { + // don't mess with controls we don't know about + // for some reason returning eventNotHandledErr does not lead to the correct behaviour + // so we try sending them the correct control directly + if ( cEvent.GetKind() == kEventMouseDown && toplevelWindow && control ) + { + EventModifiers modifiers = cEvent.GetParameter(kEventParamKeyModifiers, typeUInt32) ; + Point clickLocation = windowMouseLocation ; + + HIPoint hiPoint ; + hiPoint.x = clickLocation.h ; + hiPoint.y = clickLocation.v ; + HIViewConvertPoint( &hiPoint , (ControlRef) toplevelWindow->GetHandle() , control ) ; + clickLocation.h = (int)hiPoint.x ; + clickLocation.v = (int)hiPoint.y ; + + HandleControlClick( control , clickLocation , modifiers , (ControlActionUPP ) -1 ) ; + result = noErr ; + } + } + + return result ; +} + +static pascal OSStatus +wxNonOwnedWindowEventHandler(EventHandlerCallRef WXUNUSED(handler), + EventRef event, + void *data) +{ + OSStatus result = eventNotHandledErr ; + + wxMacCarbonEvent cEvent( event ) ; + + // WindowRef windowRef = cEvent.GetParameter(kEventParamDirectObject) ; + wxNonOwnedWindow* toplevelWindow = (wxNonOwnedWindow*) data ; + + switch ( GetEventKind( event ) ) + { + case kEventWindowActivated : + { + toplevelWindow->MacActivate( cEvent.GetTicks() , true) ; + wxActivateEvent wxevent(wxEVT_ACTIVATE, true , toplevelWindow->GetId()); + wxevent.SetTimestamp( cEvent.GetTicks() ) ; + wxevent.SetEventObject(toplevelWindow); + toplevelWindow->HandleWindowEvent(wxevent); + // we still sending an eventNotHandledErr in order to allow for default processing + } + break ; + + case kEventWindowDeactivated : + { + toplevelWindow->MacActivate(cEvent.GetTicks() , false) ; + wxActivateEvent wxevent(wxEVT_ACTIVATE, false , toplevelWindow->GetId()); + wxevent.SetTimestamp( cEvent.GetTicks() ) ; + wxevent.SetEventObject(toplevelWindow); + toplevelWindow->HandleWindowEvent(wxevent); + // we still sending an eventNotHandledErr in order to allow for default processing + } + break ; + + case kEventWindowShown : + toplevelWindow->Refresh() ; + result = noErr ; + break ; + + case kEventWindowClose : + toplevelWindow->Close() ; + result = noErr ; + break ; + + case kEventWindowBoundsChanged : + { + UInt32 attributes = cEvent.GetParameter(kEventParamAttributes, typeUInt32) ; + Rect newRect = cEvent.GetParameter(kEventParamCurrentBounds) ; + wxRect r( newRect.left , newRect.top , newRect.right - newRect.left , newRect.bottom - newRect.top ) ; + if ( attributes & kWindowBoundsChangeSizeChanged ) + { +#ifndef __WXUNIVERSAL__ + // according to the other ports we handle this within the OS level + // resize event, not within a wxSizeEvent + wxFrame *frame = wxDynamicCast( toplevelWindow , wxFrame ) ; + if ( frame ) + { + frame->PositionBars(); + } +#endif + wxSizeEvent event( r.GetSize() , toplevelWindow->GetId() ) ; + event.SetEventObject( toplevelWindow ) ; + + toplevelWindow->HandleWindowEvent(event) ; + toplevelWindow->wxWindowMac::MacSuperChangedPosition() ; // like this only children will be notified + } + + if ( attributes & kWindowBoundsChangeOriginChanged ) + { + wxMoveEvent event( r.GetLeftTop() , toplevelWindow->GetId() ) ; + event.SetEventObject( toplevelWindow ) ; + toplevelWindow->HandleWindowEvent(event) ; + } + + result = noErr ; + } + break ; + + case kEventWindowBoundsChanging : + { + UInt32 attributes = cEvent.GetParameter(kEventParamAttributes,typeUInt32) ; + Rect newRect = cEvent.GetParameter(kEventParamCurrentBounds) ; + + if ( (attributes & kWindowBoundsChangeSizeChanged) || (attributes & kWindowBoundsChangeOriginChanged) ) + { + // all (Mac) rects are in content area coordinates, all wxRects in structure coordinates + int left , top , right , bottom ; + toplevelWindow->MacGetContentAreaInset( left , top , right , bottom ) ; + + wxRect r( + newRect.left - left, + newRect.top - top, + newRect.right - newRect.left + left + right, + newRect.bottom - newRect.top + top + bottom ) ; + + // this is a EVT_SIZING not a EVT_SIZE type ! + wxSizeEvent wxevent( r , toplevelWindow->GetId() ) ; + wxevent.SetEventObject( toplevelWindow ) ; + wxRect adjustR = r ; + if ( toplevelWindow->HandleWindowEvent(wxevent) ) + adjustR = wxevent.GetRect() ; + + if ( toplevelWindow->GetMaxWidth() != -1 && adjustR.GetWidth() > toplevelWindow->GetMaxWidth() ) + adjustR.SetWidth( toplevelWindow->GetMaxWidth() ) ; + if ( toplevelWindow->GetMaxHeight() != -1 && adjustR.GetHeight() > toplevelWindow->GetMaxHeight() ) + adjustR.SetHeight( toplevelWindow->GetMaxHeight() ) ; + if ( toplevelWindow->GetMinWidth() != -1 && adjustR.GetWidth() < toplevelWindow->GetMinWidth() ) + adjustR.SetWidth( toplevelWindow->GetMinWidth() ) ; + if ( toplevelWindow->GetMinHeight() != -1 && adjustR.GetHeight() < toplevelWindow->GetMinHeight() ) + adjustR.SetHeight( toplevelWindow->GetMinHeight() ) ; + const Rect adjustedRect = { adjustR.y + top , adjustR.x + left , adjustR.y + adjustR.height - bottom , adjustR.x + adjustR.width - right } ; + if ( !EqualRect( &newRect , &adjustedRect ) ) + cEvent.SetParameter( kEventParamCurrentBounds , &adjustedRect ) ; + toplevelWindow->wxWindowMac::MacSuperChangedPosition() ; // like this only children will be notified + } + + result = noErr ; + } + break ; + + case kEventWindowGetRegion : + { + if ( toplevelWindow->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT ) + { + WindowRegionCode windowRegionCode ; + + // Fetch the region code that is being queried + GetEventParameter( event, + kEventParamWindowRegionCode, + typeWindowRegionCode, NULL, + sizeof windowRegionCode, NULL, + &windowRegionCode ) ; + + // If it is the opaque region code then set the + // region to empty and return noErr to stop event + // propagation + if ( windowRegionCode == kWindowOpaqueRgn ) { + RgnHandle region; + GetEventParameter( event, + kEventParamRgnHandle, + typeQDRgnHandle, NULL, + sizeof region, NULL, + ®ion) ; + SetEmptyRgn(region) ; + result = noErr ; + } + } + } + break ; + + default : + break ; + } + + return result ; +} + +// mix this in from window.cpp +pascal OSStatus wxMacUnicodeTextEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) ; + +pascal OSStatus wxNonOwnedEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + + switch ( GetEventClass( event ) ) + { + case kEventClassTextInput : + result = wxMacUnicodeTextEventHandler( handler, event , data ) ; + break ; + + case kEventClassKeyboard : + result = KeyboardEventHandler( handler, event , data ) ; + break ; + + case kEventClassWindow : + result = wxNonOwnedWindowEventHandler( handler, event , data ) ; + break ; + + case kEventClassMouse : + result = wxMacTopLevelMouseEventHandler( handler, event , data ) ; + break ; + + default : + break ; + } + + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxNonOwnedEventHandler ) + +// --------------------------------------------------------------------------- +// wxWindowMac utility functions +// --------------------------------------------------------------------------- + +// Find an item given the Macintosh Window Reference + +WX_DECLARE_HASH_MAP(WindowRef, wxNonOwnedWindow*, wxPointerHash, wxPointerEqual, MacWindowMap); + +static MacWindowMap wxWinMacWindowList; + +wxNonOwnedWindow *wxFindWinFromMacWindow(WindowRef inWindowRef) +{ + MacWindowMap::iterator node = wxWinMacWindowList.find(inWindowRef); + + return (node == wxWinMacWindowList.end()) ? NULL : node->second; +} + +void wxAssociateWinWithMacWindow(WindowRef inWindowRef, wxNonOwnedWindow *win) ; +void wxAssociateWinWithMacWindow(WindowRef inWindowRef, wxNonOwnedWindow *win) +{ + // adding NULL WindowRef is (first) surely a result of an error and + // nothing else :-) + wxCHECK_RET( inWindowRef != (WindowRef) NULL, wxT("attempt to add a NULL WindowRef to window list") ); + + wxWinMacWindowList[inWindowRef] = win; +} + +void wxRemoveMacWindowAssociation(wxNonOwnedWindow *win) ; +void wxRemoveMacWindowAssociation(wxNonOwnedWindow *win) +{ + MacWindowMap::iterator it; + for ( it = wxWinMacWindowList.begin(); it != wxWinMacWindowList.end(); ++it ) + { + if ( it->second == win ) + { + wxWinMacWindowList.erase(it); + break; + } + } +} + +// ---------------------------------------------------------------------------- +// wxNonOwnedWindow creation +// ---------------------------------------------------------------------------- + +wxNonOwnedWindow *wxNonOwnedWindow::s_macDeactivateWindow = NULL; + +void wxNonOwnedWindow::Init() +{ + m_macWindow = NULL ; + m_macEventHandler = NULL ; +} + +wxMacDeferredWindowDeleter::wxMacDeferredWindowDeleter( WindowRef windowRef ) +{ + m_macWindow = windowRef ; +} + +wxMacDeferredWindowDeleter::~wxMacDeferredWindowDeleter() +{ + DisposeWindow( (WindowRef) m_macWindow ) ; +} + +bool wxNonOwnedWindow::Create(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) +{ + // init our fields + Init(); + + m_windowStyle = style; + + SetName( name ); + + m_windowId = id == -1 ? NewControlId() : id; + + DoMacCreateRealWindow( parent, pos , size , style , name ) ; + + SetBackgroundColour(wxSystemSettings::GetColour( wxSYS_COLOUR_3DFACE )); + + if (GetExtraStyle() & wxFRAME_EX_METAL) + MacSetMetalAppearance(true); + + if ( parent ) + parent->AddChild(this); + + return true; +} + +wxNonOwnedWindow::~wxNonOwnedWindow() +{ + if ( m_macWindow ) + { +#if wxUSE_TOOLTIPS + wxToolTip::NotifyWindowDelete(m_macWindow) ; +#endif + wxPendingDelete.Append( new wxMacDeferredWindowDeleter( (WindowRef) m_macWindow ) ) ; + } + + if ( m_macEventHandler ) + { + ::RemoveEventHandler((EventHandlerRef) m_macEventHandler); + m_macEventHandler = NULL ; + } + + wxRemoveMacWindowAssociation( this ) ; + + // avoid dangling refs + if ( s_macDeactivateWindow == this ) + s_macDeactivateWindow = NULL; +} + +// ---------------------------------------------------------------------------- +// wxNonOwnedWindow misc +// ---------------------------------------------------------------------------- + +wxPoint wxNonOwnedWindow::GetClientAreaOrigin() const +{ + return wxPoint(0, 0) ; +} + +bool wxNonOwnedWindow::SetBackgroundColour(const wxColour& c ) +{ + wxColour col = c; + if ( col == wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ) + col = wxColour(wxMacCreateCGColorFromHITheme(kThemeBrushDocumentWindowBackground)); + else if ( col == wxSystemSettings::GetColour( wxSYS_COLOUR_3DFACE ) ) + col = wxColour(wxMacCreateCGColorFromHITheme(kThemeBrushDialogBackgroundActive)); + + if ( !wxWindow::SetBackgroundColour(col) && m_hasBgCol ) + return false ; + + if ( GetBackgroundStyle() != wxBG_STYLE_CUSTOM ) + { + if ( col == wxColour(wxMacCreateCGColorFromHITheme(kThemeBrushDocumentWindowBackground)) ) + { + SetThemeWindowBackground( (WindowRef) m_macWindow, kThemeBrushDocumentWindowBackground, false ) ; + SetBackgroundStyle(wxBG_STYLE_SYSTEM); + } + else if ( col == wxColour(wxMacCreateCGColorFromHITheme(kThemeBrushDialogBackgroundActive)) ) + { + SetThemeWindowBackground( (WindowRef) m_macWindow, kThemeBrushDialogBackgroundActive, false ) ; + SetBackgroundStyle(wxBG_STYLE_SYSTEM); + } + } + return true; +} + +void wxNonOwnedWindowInstallTopLevelWindowEventHandler(WindowRef window, EventHandlerRef* handler, void *ref) +{ + InstallWindowEventHandler(window, GetwxNonOwnedEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, ref, handler ); +} + +void wxNonOwnedWindow::MacInstallTopLevelWindowEventHandler() +{ + if ( m_macEventHandler != NULL ) + { + verify_noerr( ::RemoveEventHandler( (EventHandlerRef) m_macEventHandler ) ) ; + } + wxNonOwnedWindowInstallTopLevelWindowEventHandler(MAC_WXHWND(m_macWindow),(EventHandlerRef *)&m_macEventHandler,this); +} + +void wxNonOwnedWindow::MacCreateRealWindow( + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + DoMacCreateRealWindow( NULL, pos, size, style, name ); +} + +void wxNonOwnedWindow::DoMacCreateRealWindow( + wxWindow* parent, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + OSStatus err = noErr ; + SetName(name); + m_windowStyle = style; + m_isShown = false; + + // create frame. + int x = (int)pos.x; + int y = (int)pos.y; + + Rect theBoundsRect; + wxRect display = wxGetClientDisplayRect() ; + + if ( x == wxDefaultPosition.x ) + x = display.x ; + + if ( y == wxDefaultPosition.y ) + y = display.y ; + + int w = WidthDefault(size.x); + int h = HeightDefault(size.y); + + ::SetRect(&theBoundsRect, x, y , x + w, y + h); + + // translate the window attributes in the appropriate window class and attributes + WindowClass wclass = 0; + WindowAttributes attr = kWindowNoAttributes ; + WindowGroupRef group = NULL ; + bool activationScopeSet = false; + WindowActivationScope activationScope = kWindowActivationScopeNone; + + if ( HasFlag( wxFRAME_TOOL_WINDOW) ) + { + if ( + HasFlag( wxMINIMIZE_BOX ) || HasFlag( wxMAXIMIZE_BOX ) || + HasFlag( wxSYSTEM_MENU ) || HasFlag( wxCAPTION ) || + HasFlag(wxTINY_CAPTION_HORIZ) || HasFlag(wxTINY_CAPTION_VERT) + ) + { + if ( HasFlag( wxSTAY_ON_TOP ) ) + wclass = kUtilityWindowClass; + else + wclass = kFloatingWindowClass ; + + if ( HasFlag(wxTINY_CAPTION_VERT) ) + attr |= kWindowSideTitlebarAttribute ; + } + else + { + wclass = kPlainWindowClass ; + activationScopeSet = true; + activationScope = kWindowActivationScopeNone; + } + } + else if ( HasFlag( wxPOPUP_WINDOW ) ) + { + if ( HasFlag( wxBORDER_NONE ) ) + { + wclass = kHelpWindowClass ; // has no border + attr |= kWindowNoShadowAttribute; + } + else + { + wclass = kPlainWindowClass ; // has a single line border, it will have to do for now + } + group = GetWindowGroupOfClass(kFloatingWindowClass) ; + // make sure we don't deactivate something + activationScopeSet = true; + activationScope = kWindowActivationScopeNone; + } + else if ( HasFlag( wxCAPTION ) ) + { + wclass = kDocumentWindowClass ; + attr |= kWindowInWindowMenuAttribute ; + } + else if ( HasFlag( wxFRAME_DRAWER ) ) + { + wclass = kDrawerWindowClass; + } + else + { + if ( HasFlag( wxMINIMIZE_BOX ) || HasFlag( wxMAXIMIZE_BOX ) || + HasFlag( wxCLOSE_BOX ) || HasFlag( wxSYSTEM_MENU ) ) + { + wclass = kDocumentWindowClass ; + } + else if ( HasFlag( wxNO_BORDER ) ) + { + wclass = kSimpleWindowClass ; + } + else + { + wclass = kPlainWindowClass ; + } + } + + if ( wclass != kPlainWindowClass ) + { + if ( HasFlag( wxMINIMIZE_BOX ) ) + attr |= kWindowCollapseBoxAttribute ; + + if ( HasFlag( wxMAXIMIZE_BOX ) ) + attr |= kWindowFullZoomAttribute ; + + if ( HasFlag( wxRESIZE_BORDER ) ) + attr |= kWindowResizableAttribute ; + + if ( HasFlag( wxCLOSE_BOX) ) + attr |= kWindowCloseBoxAttribute ; + } + attr |= kWindowLiveResizeAttribute; + + if ( HasFlag(wxSTAY_ON_TOP) ) + group = GetWindowGroupOfClass(kUtilityWindowClass) ; + + if ( HasFlag( wxFRAME_FLOAT_ON_PARENT ) ) + group = GetWindowGroupOfClass(kFloatingWindowClass) ; + + if ( group == NULL && parent != NULL ) + { + WindowRef parenttlw = (WindowRef) parent->MacGetTopLevelWindowRef(); + if( parenttlw ) + group = GetWindowGroupParent( GetWindowGroup( parenttlw ) ); + } + + attr |= kWindowCompositingAttribute; +#if 0 // wxMAC_USE_CORE_GRAPHICS ; TODO : decide on overall handling of high dpi screens (pixel vs userscale) + attr |= kWindowFrameworkScaledAttribute; +#endif + + if ( HasFlag(wxFRAME_SHAPED) ) + { + WindowDefSpec customWindowDefSpec; + customWindowDefSpec.defType = kWindowDefProcPtr; + customWindowDefSpec.u.defProc = +#ifdef __LP64__ + (WindowDefUPP) wxShapedMacWindowDef; +#else + NewWindowDefUPP(wxShapedMacWindowDef); +#endif + err = ::CreateCustomWindow( &customWindowDefSpec, wclass, + attr, &theBoundsRect, + (WindowRef*) &m_macWindow); + } + else + { + err = ::CreateNewWindow( wclass , attr , &theBoundsRect , (WindowRef*)&m_macWindow ) ; + } + + if ( err == noErr && m_macWindow != NULL && group != NULL ) + SetWindowGroup( (WindowRef) m_macWindow , group ) ; + + wxCHECK_RET( err == noErr, wxT("Mac OS error when trying to create new window") ); + + // setup a separate group for each window, so that overlays can be handled easily + + WindowGroupRef overlaygroup = NULL; + verify_noerr( CreateWindowGroup( kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrHideOnCollapse, &overlaygroup )); + verify_noerr( SetWindowGroupParent( overlaygroup, GetWindowGroup( (WindowRef) m_macWindow ))); + verify_noerr( SetWindowGroup( (WindowRef) m_macWindow , overlaygroup )); + + if ( activationScopeSet ) + { + verify_noerr( SetWindowActivationScope( (WindowRef) m_macWindow , activationScope )); + } + + // the create commands are only for content rect, + // so we have to set the size again as structure bounds + SetWindowBounds( (WindowRef) m_macWindow , kWindowStructureRgn , &theBoundsRect ) ; + + wxAssociateWinWithMacWindow( (WindowRef) m_macWindow , this ) ; + m_peer = new wxMacControl(this , true /*isRootControl*/) ; + + // There is a bug in 10.2.X for ::GetRootControl returning the window view instead of + // the content view, so we have to retrieve it explicitly + HIViewFindByID( HIViewGetRoot( (WindowRef) m_macWindow ) , kHIViewWindowContentID , + m_peer->GetControlRefAddr() ) ; + if ( !m_peer->Ok() ) + { + // compatibility mode fallback + GetRootControl( (WindowRef) m_macWindow , m_peer->GetControlRefAddr() ) ; + } + + // the root control level handler + MacInstallEventHandler( (WXWidget) m_peer->GetControlRef() ) ; + + // Causes the inner part of the window not to be metal + // if the style is used before window creation. +#if 0 // TARGET_API_MAC_OSX + if ( m_macUsesCompositing && m_macWindow != NULL ) + { + if ( GetExtraStyle() & wxFRAME_EX_METAL ) + MacSetMetalAppearance( true ) ; + } +#endif + + if ( m_macWindow != NULL ) + { + MacSetUnifiedAppearance( true ) ; + } + + HIViewRef growBoxRef = 0 ; + err = HIViewFindByID( HIViewGetRoot( (WindowRef)m_macWindow ), kHIViewWindowGrowBoxID, &growBoxRef ); + if ( err == noErr && growBoxRef != 0 ) + HIGrowBoxViewSetTransparent( growBoxRef, true ) ; + + // the frame window event handler + InstallStandardEventHandler( GetWindowEventTarget(MAC_WXHWND(m_macWindow)) ) ; + MacInstallTopLevelWindowEventHandler() ; + + DoSetWindowVariant( m_windowVariant ) ; + + m_macFocus = NULL ; + + if ( HasFlag(wxFRAME_SHAPED) ) + { + // default shape matches the window size + wxRegion rgn( 0, 0, w, h ); + SetShape( rgn ); + } + + wxWindowCreateEvent event(this); + HandleWindowEvent(event); +} + +// Raise the window to the top of the Z order +void wxNonOwnedWindow::Raise() +{ + ::SelectWindow( (WindowRef)m_macWindow ) ; +} + +// Lower the window to the bottom of the Z order +void wxNonOwnedWindow::Lower() +{ + ::SendBehind( (WindowRef)m_macWindow , NULL ) ; +} + +void wxNonOwnedWindow::MacDelayedDeactivation(long timestamp) +{ + if (s_macDeactivateWindow) + { + wxLogTrace(TRACE_ACTIVATE, + wxT("Doing delayed deactivation of %p"), + s_macDeactivateWindow); + + s_macDeactivateWindow->MacActivate(timestamp, false); + } +} + +void wxNonOwnedWindow::MacActivate( long timestamp , bool WXUNUSED(inIsActivating) ) +{ + wxLogTrace(TRACE_ACTIVATE, wxT("TopLevel=%p::MacActivate"), this); + + if (s_macDeactivateWindow == this) + s_macDeactivateWindow = NULL; + + MacDelayedDeactivation(timestamp); +} + +bool wxNonOwnedWindow::Show(bool show) +{ + if ( !wxWindow::Show(show) ) + return false; + + bool plainTransition = true; + +#if wxUSE_SYSTEM_OPTIONS + if ( wxSystemOptions::HasOption(wxMAC_WINDOW_PLAIN_TRANSITION) ) + plainTransition = ( wxSystemOptions::GetOptionInt( wxMAC_WINDOW_PLAIN_TRANSITION ) == 1 ) ; +#endif + + if (show) + { + if ( plainTransition ) + ::ShowWindow( (WindowRef)m_macWindow ); + else + ::TransitionWindow( (WindowRef)m_macWindow, kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL ); + + ::SelectWindow( (WindowRef)m_macWindow ) ; + + // because apps expect a size event to occur at this moment + wxSizeEvent event(GetSize() , m_windowId); + event.SetEventObject(this); + HandleWindowEvent(event); + } + else + { + if ( plainTransition ) + ::HideWindow( (WindowRef)m_macWindow ); + else + ::TransitionWindow( (WindowRef)m_macWindow, kWindowZoomTransitionEffect, kWindowHideTransitionAction, NULL ); + } + + return true ; +} + +bool wxNonOwnedWindow::MacShowWithEffect(bool show, + wxShowEffect effect, + unsigned timeout) +{ + if ( !wxWindow::Show(show) ) + return false; + + WindowTransitionEffect transition = 0 ; + switch( effect ) + { + case wxSHOW_EFFECT_ROLL_TO_LEFT: + case wxSHOW_EFFECT_ROLL_TO_RIGHT: + case wxSHOW_EFFECT_ROLL_TO_TOP: + case wxSHOW_EFFECT_ROLL_TO_BOTTOM: + case wxSHOW_EFFECT_SLIDE_TO_LEFT: + case wxSHOW_EFFECT_SLIDE_TO_RIGHT: + case wxSHOW_EFFECT_SLIDE_TO_TOP: + case wxSHOW_EFFECT_SLIDE_TO_BOTTOM: + transition = kWindowGenieTransitionEffect; + break; + case wxSHOW_EFFECT_BLEND: + transition = kWindowFadeTransitionEffect; + break; + case wxSHOW_EFFECT_EXPAND: + // having sheets would be fine, but this might lead to a repositioning +#if 0 + if ( GetParent() ) + transition = kWindowSheetTransitionEffect; + else +#endif + transition = kWindowZoomTransitionEffect; + break; + + case wxSHOW_EFFECT_MAX: + wxFAIL_MSG( "invalid effect flag" ); + return false; + } + + TransitionWindowOptions options; + options.version = 0; + options.duration = timeout / 1000.0; + options.window = transition == kWindowSheetTransitionEffect ? (WindowRef) GetParent()->MacGetTopLevelWindowRef() :0; + options.userData = 0; + + wxSize size = wxGetDisplaySize(); + Rect bounds; + GetWindowBounds( (WindowRef)m_macWindow, kWindowStructureRgn, &bounds ); + CGRect hiBounds = CGRectMake( bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top ); + + switch ( effect ) + { + case wxSHOW_EFFECT_ROLL_TO_RIGHT: + case wxSHOW_EFFECT_SLIDE_TO_RIGHT: + hiBounds.origin.x = 0; + hiBounds.size.width = 0; + break; + + case wxSHOW_EFFECT_ROLL_TO_LEFT: + case wxSHOW_EFFECT_SLIDE_TO_LEFT: + hiBounds.origin.x = size.x; + hiBounds.size.width = 0; + break; + + case wxSHOW_EFFECT_ROLL_TO_TOP: + case wxSHOW_EFFECT_SLIDE_TO_TOP: + hiBounds.origin.y = size.y; + hiBounds.size.height = 0; + break; + + case wxSHOW_EFFECT_ROLL_TO_BOTTOM: + case wxSHOW_EFFECT_SLIDE_TO_BOTTOM: + hiBounds.origin.y = 0; + hiBounds.size.height = 0; + break; + + default: + break; // direction doesn't make sense + } + + ::TransitionWindowWithOptions + ( + (WindowRef)m_macWindow, + transition, + show ? kWindowShowTransitionAction : kWindowHideTransitionAction, + transition == kWindowGenieTransitionEffect ? &hiBounds : NULL, + false, + &options + ); + + if ( show ) + { + ::SelectWindow( (WindowRef)m_macWindow ) ; + + // because apps expect a size event to occur at this moment + wxSizeEvent event(GetSize() , m_windowId); + event.SetEventObject(this); + HandleWindowEvent(event); + } + + return true; +} + +bool wxNonOwnedWindow::SetTransparent(wxByte alpha) +{ + OSStatus result = SetWindowAlpha((WindowRef)m_macWindow, (CGFloat)((alpha)/255.0)); + return result == noErr; +} + + +bool wxNonOwnedWindow::CanSetTransparent() +{ + return true; +} + + +void wxNonOwnedWindow::SetExtraStyle(long exStyle) +{ + if ( GetExtraStyle() == exStyle ) + return ; + + wxWindow::SetExtraStyle( exStyle ) ; + + if ( m_macWindow != NULL ) + { + bool metal = GetExtraStyle() & wxFRAME_EX_METAL ; + + if ( MacGetMetalAppearance() != metal ) + { + if ( MacGetUnifiedAppearance() ) + MacSetUnifiedAppearance( !metal ) ; + + MacSetMetalAppearance( metal ) ; + } + } +} + +bool wxNonOwnedWindow::SetBackgroundStyle(wxBackgroundStyle style) +{ + if ( !wxWindow::SetBackgroundStyle(style) ) + return false ; + + WindowRef windowRef = HIViewGetWindow( (HIViewRef)GetHandle() ); + + if ( GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT ) + { + OSStatus err = HIWindowChangeFeatures( windowRef, 0, kWindowIsOpaque ); + verify_noerr( err ); + err = ReshapeCustomWindow( windowRef ); + verify_noerr( err ); + } + + return true ; +} + +// TODO: switch to structure bounds - +// we are still using coordinates of the content view +// +void wxNonOwnedWindow::MacGetContentAreaInset( int &left , int &top , int &right , int &bottom ) +{ + Rect content, structure ; + + GetWindowBounds( (WindowRef) m_macWindow, kWindowStructureRgn , &structure ) ; + GetWindowBounds( (WindowRef) m_macWindow, kWindowContentRgn , &content ) ; + + left = content.left - structure.left ; + top = content.top - structure.top ; + right = structure.right - content.right ; + bottom = structure.bottom - content.bottom ; +} + +void wxNonOwnedWindow::DoMoveWindow(int x, int y, int width, int height) +{ + m_cachedClippedRectValid = false ; + Rect bounds = { y , x , y + height , x + width } ; + verify_noerr(SetWindowBounds( (WindowRef) m_macWindow, kWindowStructureRgn , &bounds )) ; + wxWindowMac::MacSuperChangedPosition() ; // like this only children will be notified +} + +void wxNonOwnedWindow::DoGetPosition( int *x, int *y ) const +{ + Rect bounds ; + + verify_noerr(GetWindowBounds((WindowRef) m_macWindow, kWindowStructureRgn , &bounds )) ; + + if (x) + *x = bounds.left ; + if (y) + *y = bounds.top ; +} + +void wxNonOwnedWindow::DoGetSize( int *width, int *height ) const +{ + Rect bounds ; + + verify_noerr(GetWindowBounds((WindowRef) m_macWindow, kWindowStructureRgn , &bounds )) ; + + if (width) + *width = bounds.right - bounds.left ; + if (height) + *height = bounds.bottom - bounds.top ; +} + +void wxNonOwnedWindow::DoGetClientSize( int *width, int *height ) const +{ + Rect bounds ; + + verify_noerr(GetWindowBounds((WindowRef) m_macWindow, kWindowContentRgn , &bounds )) ; + + if (width) + *width = bounds.right - bounds.left ; + if (height) + *height = bounds.bottom - bounds.top ; +} + +void wxNonOwnedWindow::MacSetMetalAppearance( bool set ) +{ + if ( MacGetUnifiedAppearance() ) + MacSetUnifiedAppearance( false ) ; + + MacChangeWindowAttributes( set ? kWindowMetalAttribute : kWindowNoAttributes , + set ? kWindowNoAttributes : kWindowMetalAttribute ) ; +} + +bool wxNonOwnedWindow::MacGetMetalAppearance() const +{ + return MacGetWindowAttributes() & kWindowMetalAttribute ; +} + +void wxNonOwnedWindow::MacSetUnifiedAppearance( bool set ) +{ + if ( MacGetMetalAppearance() ) + MacSetMetalAppearance( false ) ; + + MacChangeWindowAttributes( set ? kWindowUnifiedTitleAndToolbarAttribute : kWindowNoAttributes , + set ? kWindowNoAttributes : kWindowUnifiedTitleAndToolbarAttribute) ; + + // For some reason, Tiger uses white as the background color for this appearance, + // while most apps using it use the typical striped background. Restore that behavior + // for wx. + // TODO: Determine if we need this on Leopard as well. (should be harmless either way, + // though) + SetBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW) ) ; +} + +bool wxNonOwnedWindow::MacGetUnifiedAppearance() const +{ + return MacGetWindowAttributes() & kWindowUnifiedTitleAndToolbarAttribute ; +} + +void wxNonOwnedWindow::MacChangeWindowAttributes( wxUint32 attributesToSet , wxUint32 attributesToClear ) +{ + ChangeWindowAttributes( (WindowRef)m_macWindow, attributesToSet, attributesToClear ) ; +} + +wxUint32 wxNonOwnedWindow::MacGetWindowAttributes() const +{ + UInt32 attr = 0 ; + GetWindowAttributes( (WindowRef) m_macWindow, &attr ) ; + + return attr ; +} + +void wxNonOwnedWindow::MacPerformUpdates() +{ + // for composited windows this also triggers a redraw of all + // invalid views in the window + HIWindowFlush((WindowRef) m_macWindow) ; +} + +// --------------------------------------------------------------------------- +// Shape implementation +// --------------------------------------------------------------------------- + + +bool wxNonOwnedWindow::SetShape(const wxRegion& region) +{ + wxCHECK_MSG( HasFlag(wxFRAME_SHAPED), false, + _T("Shaped windows must be created with the wxFRAME_SHAPED style.")); + + // The empty region signifies that the shape + // should be removed from the window. + if ( region.IsEmpty() ) + { + wxSize sz = GetClientSize(); + wxRegion rgn(0, 0, sz.x, sz.y); + if ( rgn.IsEmpty() ) + return false ; + else + return SetShape(rgn); + } + + // Make a copy of the region + RgnHandle shapeRegion = NewRgn(); + HIShapeGetAsQDRgn( region.GetWXHRGN(), shapeRegion ); + + // Dispose of any shape region we may already have + RgnHandle oldRgn = (RgnHandle)GetWRefCon( (WindowRef)MacGetWindowRef() ); + if ( oldRgn ) + DisposeRgn(oldRgn); + + // Save the region so we can use it later + SetWRefCon((WindowRef)MacGetWindowRef(), (URefCon)shapeRegion); + + // inform the window manager that the window has changed shape + ReshapeCustomWindow((WindowRef)MacGetWindowRef()); + + return true; +} + +// --------------------------------------------------------------------------- +// Support functions for shaped windows, based on Apple's CustomWindow sample at +// http://developer.apple.com/samplecode/Sample_Code/Human_Interface_Toolbox/Mac_OS_High_Level_Toolbox/CustomWindow.htm +// --------------------------------------------------------------------------- + +static void wxShapedMacWindowGetPos(WindowRef window, Rect* inRect) +{ + GetWindowPortBounds(window, inRect); + Point pt = { inRect->top ,inRect->left }; + wxMacLocalToGlobal( window, &pt ) ; + inRect->bottom += pt.v - inRect->top; + inRect->right += pt.h - inRect->left; + inRect->top = pt.v; + inRect->left = pt.h; +} + +static SInt32 wxShapedMacWindowGetFeatures(WindowRef WXUNUSED(window), SInt32 param) +{ + /*------------------------------------------------------ + Define which options your custom window supports. + --------------------------------------------------------*/ + //just enable everything for our demo + *(OptionBits*)param = + //kWindowCanGrow | + //kWindowCanZoom | + kWindowCanCollapse | + //kWindowCanGetWindowRegion | + //kWindowHasTitleBar | + //kWindowSupportsDragHilite | + kWindowCanDrawInCurrentPort | + //kWindowCanMeasureTitle | + kWindowWantsDisposeAtProcessDeath | + kWindowSupportsGetGrowImageRegion | + kWindowDefSupportsColorGrafPort; + + return 1; +} + +// The content region is left as a rectangle matching the window size, this is +// so the origin in the paint event, and etc. still matches what the +// programmer expects. +static void wxShapedMacWindowContentRegion(WindowRef window, RgnHandle rgn) +{ + SetEmptyRgn(rgn); + wxNonOwnedWindow* win = wxFindWinFromMacWindow(window); + if (win) + { + Rect r ; + wxShapedMacWindowGetPos( window, &r ) ; + RectRgn( rgn , &r ) ; + } +} + +// The structure region is set to the shape given to the SetShape method. +static void wxShapedMacWindowStructureRegion(WindowRef window, RgnHandle rgn) +{ + RgnHandle cachedRegion = (RgnHandle) GetWRefCon(window); + + SetEmptyRgn(rgn); + if (cachedRegion) + { + Rect windowRect; + wxShapedMacWindowGetPos(window, &windowRect); // how big is the window + CopyRgn(cachedRegion, rgn); // make a copy of our cached region + OffsetRgn(rgn, windowRect.left, windowRect.top); // position it over window + //MapRgn(rgn, &mMaskSize, &windowRect); //scale it to our actual window size + } +} + +static SInt32 wxShapedMacWindowGetRegion(WindowRef window, SInt32 param) +{ + GetWindowRegionPtr rgnRec = (GetWindowRegionPtr)param; + + if (rgnRec == NULL) + return paramErr; + + switch (rgnRec->regionCode) + { + case kWindowStructureRgn: + wxShapedMacWindowStructureRegion(window, rgnRec->winRgn); + break; + + case kWindowContentRgn: + wxShapedMacWindowContentRegion(window, rgnRec->winRgn); + break; + + default: + SetEmptyRgn(rgnRec->winRgn); + break; + } + + return noErr; +} + +// Determine the region of the window which was hit +// +static SInt32 wxShapedMacWindowHitTest(WindowRef window, SInt32 param) +{ + Point hitPoint; + static RgnHandle tempRgn = NULL; + + if (tempRgn == NULL) + tempRgn = NewRgn(); + + // get the point clicked + SetPt( &hitPoint, LoWord(param), HiWord(param) ); + + // Mac OS 8.5 or later + wxShapedMacWindowStructureRegion(window, tempRgn); + if (PtInRgn( hitPoint, tempRgn )) //in window content region? + return wInContent; + + // no significant area was hit + return wNoHit; +} + +static pascal long wxShapedMacWindowDef(short WXUNUSED(varCode), WindowRef window, SInt16 message, SInt32 param) +{ + switch (message) + { + case kWindowMsgHitTest: + return wxShapedMacWindowHitTest(window, param); + + case kWindowMsgGetFeatures: + return wxShapedMacWindowGetFeatures(window, param); + + // kWindowMsgGetRegion is sent during CreateCustomWindow and ReshapeCustomWindow + case kWindowMsgGetRegion: + return wxShapedMacWindowGetRegion(window, param); + + default: + break; + } + + return 0; +} + + diff --git a/src/osx/carbon/notebmac.cpp b/src/osx/carbon/notebmac.cpp new file mode 100644 index 0000000000..6d00a7130d --- /dev/null +++ b/src/osx/carbon/notebmac.cpp @@ -0,0 +1,621 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/notebmac.cpp +// Purpose: implementation of wxNotebook +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_NOTEBOOK + +#include "wx/notebook.h" + +#ifndef WX_PRECOMP + #include "wx/string.h" + #include "wx/log.h" + #include "wx/app.h" + #include "wx/image.h" +#endif + +#include "wx/string.h" +#include "wx/imaglist.h" +#include "wx/mac/uma.h" + + +// check that the page index is valid +#define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount()) + + +DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING) + +BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase) + EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, wxNotebook::OnSelChange) + + EVT_SIZE(wxNotebook::OnSize) + EVT_SET_FOCUS(wxNotebook::OnSetFocus) + EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey) +END_EVENT_TABLE() + +IMPLEMENT_DYNAMIC_CLASS(wxNotebook, wxBookCtrlBase) +IMPLEMENT_DYNAMIC_CLASS(wxNotebookEvent, wxCommandEvent) + + +// common part of all ctors +void wxNotebook::Init() +{ + m_nSelection = -1; +} + +// default for dynamic class +wxNotebook::wxNotebook() +{ + Init(); +} + +// the same arguments as for wxControl +wxNotebook::wxNotebook( wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + Init(); + + Create( parent, id, pos, size, style, name ); +} + +bool wxNotebook::Create( wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + m_macIsUserPane = false ; + + if (! (style & wxBK_ALIGN_MASK)) + style |= wxBK_TOP; + + if ( !wxNotebookBase::Create( parent, id, pos, size, style, name ) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + + if ( bounds.right <= bounds.left ) + bounds.right = bounds.left + 100; + if ( bounds.bottom <= bounds.top ) + bounds.bottom = bounds.top + 100; + + UInt16 tabstyle = kControlTabDirectionNorth; + if ( HasFlag(wxBK_LEFT) ) + tabstyle = kControlTabDirectionWest; + else if ( HasFlag( wxBK_RIGHT ) ) + tabstyle = kControlTabDirectionEast; + else if ( HasFlag( wxBK_BOTTOM ) ) + tabstyle = kControlTabDirectionSouth; + + ControlTabSize tabsize; + switch (GetWindowVariant()) + { + case wxWINDOW_VARIANT_MINI: + tabsize = 3 ; + break; + + case wxWINDOW_VARIANT_SMALL: + tabsize = kControlTabSizeSmall; + break; + + default: + tabsize = kControlTabSizeLarge; + break; + } + + m_peer = new wxMacControl( this ); + OSStatus err = CreateTabsControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, + tabsize, tabstyle, 0, NULL, m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + MacPostControlCreate( pos, size ); + + return true ; +} + +// dtor +wxNotebook::~wxNotebook() +{ +} + +// ---------------------------------------------------------------------------- +// wxNotebook accessors +// ---------------------------------------------------------------------------- + +void wxNotebook::SetPadding(const wxSize& WXUNUSED(padding)) +{ + // unsupported by OS +} + +void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz)) +{ + // unsupported by OS +} + +void wxNotebook::SetPageSize(const wxSize& size) +{ + SetSize( CalcSizeFromPage( size ) ); +} + +wxSize wxNotebook::CalcSizeFromPage(const wxSize& sizePage) const +{ + return DoGetSizeFromClientSize( sizePage ); +} + +int wxNotebook::DoSetSelection(size_t nPage, int flags) +{ + wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("DoSetSelection: invalid notebook page") ); + + if ( m_nSelection == wxNOT_FOUND || nPage != (size_t)m_nSelection ) + { + if ( flags & SetSelection_SendEvent ) + { + if ( !SendPageChangingEvent(nPage) ) + { + // vetoed by program + return m_nSelection; + } + //else: program allows the page change + + SendPageChangedEvent(m_nSelection, nPage); + } + + ChangePage(m_nSelection, nPage); + } + //else: no change + + return m_nSelection; +} + +bool wxNotebook::SetPageText(size_t nPage, const wxString& strText) +{ + wxCHECK_MSG( IS_VALID_PAGE(nPage), false, wxT("SetPageText: invalid notebook page") ); + + wxNotebookPage *page = m_pages[nPage]; + page->SetLabel(wxStripMenuCodes(strText)); + MacSetupTabs(); + + return true; +} + +wxString wxNotebook::GetPageText(size_t nPage) const +{ + wxCHECK_MSG( IS_VALID_PAGE(nPage), wxEmptyString, wxT("GetPageText: invalid notebook page") ); + + wxNotebookPage *page = m_pages[nPage]; + + return page->GetLabel(); +} + +int wxNotebook::GetPageImage(size_t nPage) const +{ + wxCHECK_MSG( IS_VALID_PAGE(nPage), wxNOT_FOUND, wxT("GetPageImage: invalid notebook page") ); + + return m_images[nPage]; +} + +bool wxNotebook::SetPageImage(size_t nPage, int nImage) +{ + wxCHECK_MSG( IS_VALID_PAGE(nPage), false, + wxT("SetPageImage: invalid notebook page") ); + wxCHECK_MSG( m_imageList && nImage < m_imageList->GetImageCount(), false, + wxT("SetPageImage: invalid image index") ); + + if ( nImage != m_images[nPage] ) + { + // if the item didn't have an icon before or, on the contrary, did have + // it but has lost it now, its size will change - but if the icon just + // changes, it won't + m_images[nPage] = nImage; + + MacSetupTabs() ; + } + + return true; +} + +// ---------------------------------------------------------------------------- +// wxNotebook operations +// ---------------------------------------------------------------------------- + +// remove one page from the notebook, without deleting the window +wxNotebookPage* wxNotebook::DoRemovePage(size_t nPage) +{ + wxCHECK_MSG( IS_VALID_PAGE(nPage), NULL, + wxT("DoRemovePage: invalid notebook page") ); + + wxNotebookPage* page = m_pages[nPage] ; + m_pages.RemoveAt(nPage); + + MacSetupTabs(); + + if (m_nSelection >= (int)GetPageCount()) + m_nSelection = GetPageCount() - 1; + + if (m_nSelection >= 0) + m_pages[m_nSelection]->Show(true); + + InvalidateBestSize(); + + return page; +} + +// remove all pages +bool wxNotebook::DeleteAllPages() +{ + WX_CLEAR_ARRAY(m_pages) ; + MacSetupTabs(); + m_nSelection = -1 ; + InvalidateBestSize(); + + return true; +} + +// same as AddPage() but does it at given position +bool wxNotebook::InsertPage(size_t nPage, + wxNotebookPage *pPage, + const wxString& strText, + bool bSelect, + int imageId ) +{ + if ( !wxNotebookBase::InsertPage( nPage, pPage, strText, bSelect, imageId ) ) + return false; + + wxASSERT_MSG( pPage->GetParent() == this, wxT("notebook pages must have notebook as parent") ); + + // don't show pages by default (we'll need to adjust their size first) + pPage->Show( false ) ; + + pPage->SetLabel( wxStripMenuCodes(strText) ); + + m_images.Insert( imageId, nPage ); + + MacSetupTabs(); + + wxRect rect = GetPageRect() ; + pPage->SetSize( rect ); + if ( pPage->GetAutoLayout() ) + pPage->Layout(); + + // now deal with the selection + // --------------------------- + + // if the inserted page is before the selected one, we must update the + // index of the selected page + + if ( int(nPage) <= m_nSelection ) + { + m_nSelection++; + + // while this still is the same page showing, we need to update the tabs + m_peer->SetValue( m_nSelection + 1 ) ; + } + + // some page should be selected: either this one or the first one if there + // is still no selection + int selNew = -1; + if ( bSelect ) + selNew = nPage; + else if ( m_nSelection == -1 ) + selNew = 0; + + if ( selNew != -1 ) + SetSelection( selNew ); + + InvalidateBestSize(); + + return true; +} + +int wxNotebook::HitTest(const wxPoint& pt, long * flags) const +{ + int resultV = wxNOT_FOUND; + + const int countPages = GetPageCount(); + + // we have to convert from Client to Window relative coordinates + wxPoint adjustedPt = pt + GetClientAreaOrigin(); + // and now to HIView native ones + adjustedPt.x -= MacGetLeftBorderSize() ; + adjustedPt.y -= MacGetTopBorderSize() ; + + HIPoint hipoint= { adjustedPt.x , adjustedPt.y } ; + HIViewPartCode outPart = 0 ; + OSStatus err = HIViewGetPartHit( m_peer->GetControlRef(), &hipoint, &outPart ); + + int max = m_peer->GetMaximum() ; + if ( outPart == 0 && max > 0 ) + { + // this is a hack, as unfortunately a hit on an already selected tab returns 0, + // so we have to go some extra miles to make sure we select something different + // and try again .. + int val = m_peer->GetValue() ; + int maxval = max ; + if ( max == 1 ) + { + m_peer->SetMaximum( 2 ) ; + maxval = 2 ; + } + + if ( val == 1 ) + m_peer->SetValue( maxval ) ; + else + m_peer->SetValue( 1 ) ; + + err = HIViewGetPartHit( m_peer->GetControlRef(), &hipoint, &outPart ); + + m_peer->SetValue( val ) ; + if ( max == 1 ) + m_peer->SetMaximum( 1 ) ; + } + + if ( outPart >= 1 && outPart <= countPages ) + resultV = outPart - 1 ; + + if (flags != NULL) + { + *flags = 0; + + // we cannot differentiate better + if (resultV >= 0) + *flags |= wxBK_HITTEST_ONLABEL; + else + *flags |= wxBK_HITTEST_NOWHERE; + } + + return resultV; +} + +// Added by Mark Newsam +// When a page is added or deleted to the notebook this function updates +// information held in the control so that it matches the order +// the user would expect. +// +void wxNotebook::MacSetupTabs() +{ + m_peer->SetMaximum( GetPageCount() ) ; + + wxNotebookPage *page; + ControlTabInfoRecV1 info; + + const size_t countPages = GetPageCount(); + for (size_t ii = 0; ii < countPages; ii++) + { + page = m_pages[ii]; + info.version = kControlTabInfoVersionOne; + info.iconSuiteID = 0; + wxCFStringRef cflabel( page->GetLabel(), GetFont().GetEncoding() ) ; + info.name = cflabel ; + m_peer->SetData( ii + 1, kControlTabInfoTag, &info ) ; + + if ( GetImageList() && GetPageImage(ii) >= 0 ) + { + const wxBitmap bmap = GetImageList()->GetBitmap( GetPageImage( ii ) ) ; + if ( bmap.Ok() ) + { + ControlButtonContentInfo info ; + + wxMacCreateBitmapButton( &info, bmap ) ; + + OSStatus err = m_peer->SetData( ii + 1, kControlTabImageContentTag, &info ); + if ( err != noErr ) + { + wxFAIL_MSG("Error when setting icon on tab"); + } + + wxMacReleaseBitmapButton( &info ) ; + } + } + + m_peer->SetTabEnabled( ii + 1, true ) ; + } + + Refresh(); +} + +wxRect wxNotebook::GetPageRect() const +{ + wxSize size = GetClientSize() ; + + return wxRect( 0 , 0 , size.x , size.y ) ; +} + +// ---------------------------------------------------------------------------- +// wxNotebook callbacks +// ---------------------------------------------------------------------------- + +// @@@ OnSize() is used for setting the font when it's called for the first +// time because doing it in ::Create() doesn't work (for unknown reasons) +void wxNotebook::OnSize(wxSizeEvent& event) +{ + unsigned int nCount = m_pages.Count(); + wxRect rect = GetPageRect() ; + + for ( unsigned int nPage = 0; nPage < nCount; nPage++ ) + { + wxNotebookPage *pPage = m_pages[nPage]; + pPage->SetSize(rect); + if ( pPage->GetAutoLayout() ) + pPage->Layout(); + } + + // Processing continues to next OnSize + event.Skip(); +} + +void wxNotebook::OnSelChange(wxNotebookEvent& event) +{ + // is it our tab control? + if ( event.GetEventObject() == this ) + ChangePage(event.GetOldSelection(), event.GetSelection()); + + // we want to give others a chance to process this message as well + event.Skip(); +} + +void wxNotebook::OnSetFocus(wxFocusEvent& event) +{ + // set focus to the currently selected page if any + if ( m_nSelection != -1 ) + m_pages[m_nSelection]->SetFocus(); + + event.Skip(); +} + +void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event) +{ + if ( event.IsWindowChange() ) + { + // change pages + AdvanceSelection( event.GetDirection() ); + } + else + { + // we get this event in 2 cases + // + // a) one of our pages might have generated it because the user TABbed + // out from it in which case we should propagate the event upwards and + // our parent will take care of setting the focus to prev/next sibling + // + // or + // + // b) the parent panel wants to give the focus to us so that we + // forward it to our selected page. We can't deal with this in + // OnSetFocus() because we don't know which direction the focus came + // from in this case and so can't choose between setting the focus to + // first or last panel child + wxWindow *parent = GetParent(); + + // the cast is here to fix a GCC ICE + if ( ((wxWindow*)event.GetEventObject()) == parent ) + { + // no, it doesn't come from child, case (b): forward to a page + if ( m_nSelection != -1 ) + { + // so that the page knows that the event comes from it's parent + // and is being propagated downwards + event.SetEventObject( this ); + + wxWindow *page = m_pages[m_nSelection]; + if ( !page->HandleWindowEvent( event ) ) + { + page->SetFocus(); + } + //else: page manages focus inside it itself + } + else + { + // we have no pages - still have to give focus to _something_ + SetFocus(); + } + } + else + { + // it comes from our child, case (a), pass to the parent + if ( parent ) + { + event.SetCurrentFocus( this ); + parent->HandleWindowEvent( event ); + } + } + } +} + +// ---------------------------------------------------------------------------- +// wxNotebook base class virtuals +// ---------------------------------------------------------------------------- + +#if wxUSE_CONSTRAINTS + +// override these 2 functions to do nothing: everything is done in OnSize + +void wxNotebook::SetConstraintSizes(bool WXUNUSED(recurse)) +{ + // don't set the sizes of the pages - their correct size is not yet known + wxControl::SetConstraintSizes( false ); +} + +bool wxNotebook::DoPhase(int WXUNUSED(nPhase)) +{ + return true; +} + +#endif // wxUSE_CONSTRAINTS + +void wxNotebook::Command(wxCommandEvent& WXUNUSED(event)) +{ + wxFAIL_MSG(wxT("wxNotebook::Command not implemented")); +} + +// ---------------------------------------------------------------------------- +// wxNotebook helper functions +// ---------------------------------------------------------------------------- + +// hide the currently active panel and show the new one +void wxNotebook::ChangePage(int nOldSel, int nSel) +{ + if (nOldSel == nSel) + return; + + if ( nOldSel != -1 ) + m_pages[nOldSel]->Show( false ); + + if ( nSel != -1 ) + { + wxNotebookPage *pPage = m_pages[nSel]; + pPage->Show( true ); + pPage->SetFocus(); + } + + m_nSelection = nSel; + m_peer->SetValue( m_nSelection + 1 ) ; +} + +wxInt32 wxNotebook::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + OSStatus status = eventNotHandledErr ; + + SInt32 newSel = m_peer->GetValue() - 1 ; + if ( newSel != m_nSelection ) + { + wxNotebookEvent changing( + wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING, m_windowId, + newSel , m_nSelection ); + changing.SetEventObject( this ); + HandleWindowEvent( changing ); + + if ( changing.IsAllowed() ) + { + wxNotebookEvent event( + wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, m_windowId, + newSel, m_nSelection ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + } + else + { + m_peer->SetValue( m_nSelection + 1 ) ; + } + + status = noErr ; + } + + return (wxInt32)status ; +} + +#endif diff --git a/src/osx/carbon/overlay.cpp b/src/osx/carbon/overlay.cpp new file mode 100644 index 0000000000..3ae300276a --- /dev/null +++ b/src/osx/carbon/overlay.cpp @@ -0,0 +1,185 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/overlay.cpp +// Purpose: common wxOverlay code +// Author: Stefan Csomor +// Modified by: +// Created: 2006-10-20 +// RCS-ID: $Id$ +// Copyright: (c) wxWidgets team +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/overlay.h" + +#ifndef WX_PRECOMP + #include "wx/dcclient.h" +#endif + +#include "wx/private/overlay.h" + +#ifdef wxHAS_NATIVE_OVERLAY + +// ============================================================================ +// implementation +// ============================================================================ + +wxOverlayImpl::wxOverlayImpl() +{ + m_window = NULL ; + m_overlayContext = NULL ; + m_overlayWindow = NULL ; +} + +wxOverlayImpl::~wxOverlayImpl() +{ + Reset(); +} + +bool wxOverlayImpl::IsOk() +{ + return m_overlayWindow != NULL ; +} + +void wxOverlayImpl::MacGetBounds( Rect *bounds ) +{ + int x, y; + x=y=0; + m_window->MacWindowToRootWindow( &x , &y ) ; + WindowRef window = (WindowRef) m_window->MacGetTopLevelWindowRef() ; + + Point localwhere = { y, x }; + wxMacLocalToGlobal( window, &localwhere ) ; + + bounds->top = localwhere.v+m_y; + bounds->left = localwhere.h+m_x; + bounds->bottom = localwhere.v+m_y+m_height; + bounds->right = localwhere.h+m_x+m_width; +} + +OSStatus wxOverlayImpl::CreateOverlayWindow() +{ + OSStatus err; + + WindowAttributes overlayAttributes = kWindowIgnoreClicksAttribute; + + if ( m_window ) + { + m_overlayParentWindow =(WindowRef) m_window->MacGetTopLevelWindowRef(); + + Rect bounds ; + MacGetBounds(&bounds); + err = CreateNewWindow( kOverlayWindowClass, overlayAttributes, &bounds, &m_overlayWindow ); + if ( err == noErr ) + { + SetWindowGroup( m_overlayWindow, GetWindowGroup(m_overlayParentWindow)); // Put them in the same group so that their window layers are consistent + } + } + else + { + m_overlayParentWindow = NULL ; + CGRect cgbounds ; + cgbounds = CGDisplayBounds(CGMainDisplayID()); + Rect bounds; + bounds.top = (short)cgbounds.origin.y; + bounds.left = (short)cgbounds.origin.x; + bounds.bottom = (short)(bounds.top + cgbounds.size.height); + bounds.right = (short)(bounds.left + cgbounds.size.width); + err = CreateNewWindow( kOverlayWindowClass, overlayAttributes, &bounds, &m_overlayWindow ); + } + ShowWindow(m_overlayWindow); + return err; +} + +void wxOverlayImpl::Init( wxDC* dc, int x , int y , int width , int height ) +{ + wxASSERT_MSG( !IsOk() , _("You cannot Init an overlay twice") ); + + m_window = dc->GetWindow(); + m_x = x ; + m_y = y ; + if ( dc->IsKindOf( CLASSINFO( wxClientDC ) )) + { + wxPoint origin = m_window->GetClientAreaOrigin(); + m_x += origin.x; + m_y += origin.y; + } + m_width = width ; + m_height = height ; + + OSStatus err = CreateOverlayWindow(); + wxASSERT_MSG( err == noErr , _("Couldn't create the overlay window") ); +#ifndef __LP64__ + err = QDBeginCGContext(GetWindowPort(m_overlayWindow), &m_overlayContext); +#endif + CGContextTranslateCTM( m_overlayContext, 0, m_height ); + CGContextScaleCTM( m_overlayContext, 1, -1 ); + CGContextTranslateCTM( m_overlayContext, -m_x , -m_y ); + wxASSERT_MSG( err == noErr , _("Couldn't init the context on the overlay window") ); +} + +void wxOverlayImpl::BeginDrawing( wxDC* dc) +{ + wxDCImpl *impl = dc->GetImpl(); + wxGCDCImpl *win_impl = wxDynamicCast(impl,wxGCDCImpl); + if (win_impl) + { + win_impl->SetGraphicsContext( wxGraphicsContext::CreateFromNative( m_overlayContext ) ); + dc->SetClippingRegion( m_x , m_y , m_width , m_height ) ; + } +} + +void wxOverlayImpl::EndDrawing( wxDC* dc) +{ + wxDCImpl *impl = dc->GetImpl(); + wxGCDCImpl *win_impl = wxDynamicCast(impl,wxGCDCImpl); + if (win_impl) + win_impl->SetGraphicsContext(NULL); + + CGContextFlush( m_overlayContext ); +} + +void wxOverlayImpl::Clear(wxDC* WXUNUSED(dc)) +{ + wxASSERT_MSG( IsOk() , _("You cannot Clear an overlay that is not inited") ); + CGRect box = CGRectMake( m_x - 1, m_y - 1 , m_width + 2 , m_height + 2 ); + CGContextClearRect( m_overlayContext, box ); +} + +void wxOverlayImpl::Reset() +{ + if ( m_overlayContext ) + { +#ifndef __LP64__ + OSStatus err = QDEndCGContext(GetWindowPort(m_overlayWindow), &m_overlayContext); + if ( err != noErr ) + { + wxFAIL_MSG("Couldn't end the context on the overlay window"); + } +#endif + m_overlayContext = NULL ; + } + + // todo : don't dispose, only hide and reposition on next run + if (m_overlayWindow) + { + DisposeWindow(m_overlayWindow); + m_overlayWindow = NULL ; + } +} + +#endif // wxHAS_NATIVE_OVERLAY diff --git a/src/osx/carbon/palette.cpp b/src/osx/carbon/palette.cpp new file mode 100644 index 0000000000..37b1826a80 --- /dev/null +++ b/src/osx/carbon/palette.cpp @@ -0,0 +1,155 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/palette.cpp +// Purpose: wxPalette +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_PALETTE + +#include "wx/palette.h" +#include "wx/colour.h" + +IMPLEMENT_DYNAMIC_CLASS(wxPalette, wxGDIObject) + +// ============================================================================ +// wxPaletteRefData +// ============================================================================ + +class WXDLLEXPORT wxPaletteRefData: public wxGDIRefData +{ +public: + wxPaletteRefData(); + wxPaletteRefData(const wxPaletteRefData& data); + virtual ~wxPaletteRefData(); + + virtual bool IsOk() const { return m_count > 0; } + +protected: + wxColour* m_palette; + wxInt32 m_count; + + friend class WXDLLIMPEXP_FWD_CORE wxPalette; + + DECLARE_NO_ASSIGN_CLASS(wxPaletteRefData) +}; + +wxPaletteRefData::wxPaletteRefData() +{ + m_palette = NULL; + m_count = 0; +} + +wxPaletteRefData::wxPaletteRefData(const wxPaletteRefData& data) +{ + m_count = data.m_count; + m_palette = new wxColour[m_count]; + for ( wxInt32 i = 0; i < m_count; i++ ) + m_palette[i] = data.m_palette[i]; +} + +wxPaletteRefData::~wxPaletteRefData() +{ + delete[] m_palette; +} + +// ============================================================================ +// wxPalette +// ============================================================================ + +wxPalette::wxPalette() +{ +} + +wxPalette::wxPalette(int n, const unsigned char *red, const unsigned char *green, const unsigned char *blue) +{ + Create(n, red, green, blue); +} + +wxPalette::~wxPalette() +{ +} + +bool wxPalette::Create(int n, const unsigned char *red, const unsigned char *green, const unsigned char *blue) +{ + UnRef(); + + m_refData = new wxPaletteRefData; + + M_PALETTEDATA->m_count = n ; + M_PALETTEDATA->m_palette = new wxColour[n] ; + + for ( int i = 0 ; i < n ; ++i) + { + M_PALETTEDATA->m_palette[i].Set( red[i] , green[i] , blue[i] ) ; + } + + return false; +} + +int wxPalette::GetPixel(unsigned char red, unsigned char green, unsigned char blue) const +{ + if ( !m_refData ) + return wxNOT_FOUND; + + long bestdiff = 3 * 256 ; + long bestpos = 0 ; + long currentdiff ; + + for ( int i = 0 ; i < M_PALETTEDATA->m_count ; ++i ) + { + const wxColour& col = M_PALETTEDATA->m_palette[i] ; + currentdiff = abs ( col.Red() - red ) + abs( col.Green() - green ) + abs ( col.Blue() - blue ) ; + if ( currentdiff < bestdiff ) + { + bestdiff = currentdiff ; + bestpos = i ; + if ( bestdiff == 0 ) + break ; + } + } + + return bestpos; +} + +bool wxPalette::GetRGB(int index, unsigned char *red, unsigned char *green, unsigned char *blue) const +{ + if ( !m_refData ) + return false; + + if (index < 0 || index >= M_PALETTEDATA->m_count) + return false; + + const wxColour& col = M_PALETTEDATA->m_palette[index] ; + *red = col.Red() ; + *green = col.Green() ; + *blue = col.Blue() ; + + return true; +} + +int wxPalette::GetColoursCount() const +{ + if (m_refData) + return M_PALETTEDATA->m_count; + + return 0; +} + +wxGDIRefData *wxPalette::CreateGDIRefData() const +{ + return new wxPaletteRefData; +} + +wxGDIRefData *wxPalette::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxPaletteRefData(*wx_static_cast(const wxPaletteRefData *, data)); +} + +#endif // wxUSE_PALETTE diff --git a/src/osx/carbon/pen.cpp b/src/osx/carbon/pen.cpp new file mode 100644 index 0000000000..260f1abc82 --- /dev/null +++ b/src/osx/carbon/pen.cpp @@ -0,0 +1,308 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/pen.cpp +// Purpose: wxPen +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/pen.h" + +#ifndef WX_PRECOMP + #include "wx/utils.h" +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxPen, wxGDIObject) + +class WXDLLEXPORT wxPenRefData : public wxGDIRefData +{ +public: + wxPenRefData(); + wxPenRefData(const wxPenRefData& data); + virtual ~wxPenRefData(); + + wxPenRefData& operator=(const wxPenRefData& data); + + bool operator==(const wxPenRefData& data) const + { + // we intentionally don't compare m_hPen fields here + return m_style == data.m_style && + m_width == data.m_width && + m_join == data.m_join && + m_cap == data.m_cap && + m_colour == data.m_colour && + (m_style != wxPENSTYLE_STIPPLE || m_stipple.IsSameAs(data.m_stipple)) && + (m_style != wxPENSTYLE_USER_DASH || + (m_nbDash == data.m_nbDash && + memcmp(m_dash, data.m_dash, m_nbDash*sizeof(wxDash)) == 0)); + } + +protected: + int m_width; + wxPenStyle m_style; + wxPenJoin m_join ; + wxPenCap m_cap ; + wxBitmap m_stipple ; + int m_nbDash ; + wxDash * m_dash ; + wxColour m_colour; + /* TODO: implementation + WXHPEN m_hPen; + */ + + friend class WXDLLIMPEXP_FWD_CORE wxPen; +}; + +wxPenRefData::wxPenRefData() +{ + m_style = wxPENSTYLE_SOLID; + m_width = 1; + m_join = wxJOIN_ROUND ; + m_cap = wxCAP_ROUND ; + m_nbDash = 0 ; + m_dash = 0 ; +} + +wxPenRefData::wxPenRefData(const wxPenRefData& data) +: wxGDIRefData() +{ + m_style = data.m_style; + m_width = data.m_width; + m_join = data.m_join; + m_cap = data.m_cap; + m_nbDash = data.m_nbDash; + m_dash = data.m_dash; + m_colour = data.m_colour; +} + +wxPenRefData::~wxPenRefData() +{ +} + +// Pens + +#define M_PENDATA ((wxPenRefData *)m_refData) + +wxPen::wxPen() +{ +} + +wxPen::~wxPen() +{ +} + +// Should implement Create +wxPen::wxPen(const wxColour& col, int Width, wxPenStyle Style) +{ + m_refData = new wxPenRefData; + + M_PENDATA->m_colour = col; + M_PENDATA->m_width = Width; + M_PENDATA->m_style = Style; + M_PENDATA->m_join = wxJOIN_ROUND ; + M_PENDATA->m_cap = wxCAP_ROUND ; + M_PENDATA->m_nbDash = 0 ; + M_PENDATA->m_dash = 0 ; + + RealizeResource(); +} + +#if FUTURE_WXWIN_COMPATIBILITY_3_0 +wxPen::wxPen(const wxColour& col, int Width, int Style) +{ + m_refData = new wxPenRefData; + + M_PENDATA->m_colour = col; + M_PENDATA->m_width = Width; + M_PENDATA->m_style = (wxPenStyle)Style; + M_PENDATA->m_join = wxJOIN_ROUND ; + M_PENDATA->m_cap = wxCAP_ROUND ; + M_PENDATA->m_nbDash = 0 ; + M_PENDATA->m_dash = 0 ; + + RealizeResource(); +} +#endif + +wxPen::wxPen(const wxBitmap& stipple, int Width) +{ + m_refData = new wxPenRefData; + + M_PENDATA->m_stipple = stipple; + M_PENDATA->m_width = Width; + M_PENDATA->m_style = wxPENSTYLE_STIPPLE; + M_PENDATA->m_join = wxJOIN_ROUND ; + M_PENDATA->m_cap = wxCAP_ROUND ; + M_PENDATA->m_nbDash = 0 ; + M_PENDATA->m_dash = 0 ; + + RealizeResource(); +} + +wxGDIRefData *wxPen::CreateGDIRefData() const +{ + return new wxPenRefData; +} + +wxGDIRefData *wxPen::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxPenRefData(*wx_static_cast(const wxPenRefData *, data)); +} + +bool wxPen::operator==(const wxPen& pen) const +{ + const wxPenRefData *penData = (wxPenRefData *)pen.m_refData; + + // an invalid pen is only equal to another invalid pen + return m_refData ? penData && *M_PENDATA == *penData : !penData; +} + +wxColour wxPen::GetColour() const +{ + wxCHECK_MSG( Ok(), wxNullColour, wxT("invalid pen") ); + + return M_PENDATA->m_colour; +} + +int wxPen::GetWidth() const +{ + wxCHECK_MSG( Ok(), -1, wxT("invalid pen") ); + + return M_PENDATA->m_width; +} + +wxPenStyle wxPen::GetStyle() const +{ + wxCHECK_MSG( Ok(), wxPENSTYLE_INVALID, wxT("invalid pen") ); + + return M_PENDATA->m_style; +} + +wxPenJoin wxPen::GetJoin() const +{ + wxCHECK_MSG( Ok(), wxJOIN_INVALID, wxT("invalid pen") ); + + return M_PENDATA->m_join; +} + +wxPenCap wxPen::GetCap() const +{ + wxCHECK_MSG( Ok(), wxCAP_INVALID, wxT("invalid pen") ); + + return M_PENDATA->m_cap; +} + +int wxPen::GetDashes(wxDash **ptr) const +{ + wxCHECK_MSG( Ok(), -1, wxT("invalid pen") ); + + *ptr = M_PENDATA->m_dash; + return M_PENDATA->m_nbDash; +} + +wxBitmap *wxPen::GetStipple() const +{ + wxCHECK_MSG( Ok(), NULL, wxT("invalid pen") ); + + return &M_PENDATA->m_stipple; +} + +void wxPen::Unshare() +{ + // Don't change shared data + if (!m_refData) + { + m_refData = new wxPenRefData(); + } + else + { + wxPenRefData* ref = new wxPenRefData(*(wxPenRefData*)m_refData); + UnRef(); + m_refData = ref; + } +} + +void wxPen::SetColour(const wxColour& col) +{ + Unshare(); + + M_PENDATA->m_colour = col; + + RealizeResource(); +} + +void wxPen::SetColour(unsigned char r, unsigned char g, unsigned char b) +{ + Unshare(); + + M_PENDATA->m_colour.Set(r, g, b); + + RealizeResource(); +} + +void wxPen::SetWidth(int Width) +{ + Unshare(); + + M_PENDATA->m_width = Width; + + RealizeResource(); +} + +void wxPen::SetStyle(wxPenStyle Style) +{ + Unshare(); + + M_PENDATA->m_style = Style; + + RealizeResource(); +} + +void wxPen::SetStipple(const wxBitmap& Stipple) +{ + Unshare(); + + M_PENDATA->m_stipple = Stipple; + M_PENDATA->m_style = wxPENSTYLE_STIPPLE; + + RealizeResource(); +} + +void wxPen::SetDashes(int nb_dashes, const wxDash *Dash) +{ + Unshare(); + + M_PENDATA->m_nbDash = nb_dashes; + M_PENDATA->m_dash = (wxDash *)Dash; + + RealizeResource(); +} + +void wxPen::SetJoin(wxPenJoin Join) +{ + Unshare(); + + M_PENDATA->m_join = Join; + + RealizeResource(); +} + +void wxPen::SetCap(wxPenCap Cap) +{ + Unshare(); + + M_PENDATA->m_cap = Cap; + + RealizeResource(); +} + +bool wxPen::RealizeResource() +{ + // nothing to do here for mac + return true; +} diff --git a/src/osx/carbon/popupwin.cpp b/src/osx/carbon/popupwin.cpp new file mode 100644 index 0000000000..c2ecd3cf59 --- /dev/null +++ b/src/osx/carbon/popupwin.cpp @@ -0,0 +1,59 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/popupwin.cpp +// Purpose: implements wxPopupWindow for wxMac +// Author: Stefan Csomor +// Modified by: +// Created: +// RCS-ID: $Id$ +// Copyright: (c) 2006 Stefan Csomor +// License: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// CAUTION : This is only experimental stuff right now + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_POPUPWIN + +#ifndef WX_PRECOMP +#endif //WX_PRECOMP + +#include "wx/popupwin.h" +#include "wx/tooltip.h" + +#include "wx/mac/private.h" + +// ============================================================================ +// implementation +// ============================================================================ + +wxPopupWindow::~wxPopupWindow() +{ +} + +bool wxPopupWindow::Create(wxWindow *parent, int flags) +{ + // popup windows are created hidden by default + Hide(); + + return wxPopupWindowBase::Create(parent) && + wxNonOwnedWindow::Create(parent, wxID_ANY, + wxDefaultPosition, wxDefaultSize, + flags | wxPOPUP_WINDOW); + +} + +#endif // #if wxUSE_POPUPWIN diff --git a/src/osx/carbon/printdlg.cpp b/src/osx/carbon/printdlg.cpp new file mode 100644 index 0000000000..66d2adf22d --- /dev/null +++ b/src/osx/carbon/printdlg.cpp @@ -0,0 +1,292 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/printdlg.cpp +// Purpose: wxPrintDialog, wxPageSetupDialog +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_PRINTING_ARCHITECTURE + +#include "wx/printdlg.h" + +#ifndef WX_PRECOMP + #include "wx/object.h" + #include "wx/dcprint.h" + #include "wx/msgdlg.h" + #include "wx/textctrl.h" + #include "wx/sizer.h" + #include "wx/stattext.h" +#endif + +#include "wx/mac/printdlg.h" +#include "wx/mac/private/print.h" +#include "wx/mac/private.h" +#include "wx/statline.h" + + +// Use generic page setup dialog: use your own native one if one exists. + +IMPLEMENT_DYNAMIC_CLASS(wxMacPrintDialog, wxPrintDialogBase) + + +wxMacPrintDialog::wxMacPrintDialog() +{ + m_dialogParent = NULL; + m_printerDC = NULL; + m_destroyDC = true; +} + +wxMacPrintDialog::wxMacPrintDialog( wxWindow *p, wxPrintDialogData *data ) +{ + Create( p, data ); +} + +wxMacPrintDialog::wxMacPrintDialog( wxWindow *p, wxPrintData *data ) +{ + wxPrintDialogData data2; + if (data != NULL) + data2 = *data; + + Create( p, &data2 ); +} + +bool wxMacPrintDialog::Create( wxWindow *p, wxPrintDialogData *data ) +{ + m_dialogParent = p; + m_printerDC = NULL; + m_destroyDC = true; + + if (data != NULL) + m_printDialogData = *data; + + return true; +} + +wxMacPrintDialog::~wxMacPrintDialog() +{ + if (m_destroyDC && m_printerDC) + { + delete m_printerDC; + m_printerDC = NULL; + } +} + +int wxMacPrintDialog::ShowModal() +{ + m_printDialogData.GetPrintData().ConvertToNative(); + ((wxMacCarbonPrintData*)m_printDialogData.GetPrintData().GetNativeData())->TransferFrom( &m_printDialogData ); + + int result = wxID_CANCEL; + +#ifdef __LP64__ + // TODO use NSPrintPanel +#else + OSErr err = noErr; + Boolean accepted; + err = PMSessionPrintDialog( + ((wxMacCarbonPrintData*)m_printDialogData.GetPrintData().GetNativeData())->m_macPrintSession, + ((wxMacCarbonPrintData*)m_printDialogData.GetPrintData().GetNativeData())->m_macPrintSettings, + ((wxMacCarbonPrintData*)m_printDialogData.GetPrintData().GetNativeData())->m_macPageFormat, + &accepted ); + + if ((err == noErr) && !accepted) + { + // user clicked Cancel button + err = kPMCancel; + } + + if (err == noErr) + { + result = wxID_OK; + } + + if ((err != noErr) && (err != kPMCancel)) + { + wxString message; + + message.Printf( wxT("Print Error %d"), err ); + wxMessageDialog dialog( NULL, message, wxEmptyString, wxICON_HAND | wxOK ); + dialog.ShowModal(); + } + + if (result == wxID_OK) + { + m_printDialogData.GetPrintData().ConvertFromNative(); + ((wxMacCarbonPrintData*)m_printDialogData.GetPrintData().GetNativeData())->TransferTo( &m_printDialogData ); + } +#endif + return result; +} + +wxDC *wxMacPrintDialog::GetPrintDC() +{ + return new wxPrinterDC( m_printDialogData.GetPrintData() ); +} + +IMPLEMENT_CLASS(wxMacPageSetupDialog, wxPageSetupDialogBase) + +wxMacPageSetupDialog::wxMacPageSetupDialog( wxWindow *p, wxPageSetupData *data ) + : wxPageSetupDialogBase() +{ + Create( p, data ); +} + +bool wxMacPageSetupDialog::Create( wxWindow *p, wxPageSetupData *data ) +{ + m_dialogParent = p; + + if (data != NULL) + m_pageSetupData = (*data); + + return true; +} + +wxMacPageSetupDialog::~wxMacPageSetupDialog() +{ +} + +wxPageSetupData& wxMacPageSetupDialog::GetPageSetupDialogData() +{ + return m_pageSetupData; +} + +int wxMacPageSetupDialog::ShowModal() +{ + m_pageSetupData.GetPrintData().ConvertToNative(); + ((wxMacCarbonPrintData*)m_pageSetupData.GetPrintData().GetNativeData())->TransferFrom( &m_pageSetupData ); + + int result = wxID_CANCEL; +#ifdef __LP64__ +#else + OSErr err = noErr; + Boolean accepted; + + err = PMSessionPageSetupDialog( + ((wxMacCarbonPrintData*)m_pageSetupData.GetPrintData().GetNativeData())->m_macPrintSession, + ((wxMacCarbonPrintData*)m_pageSetupData.GetPrintData().GetNativeData())->m_macPageFormat, + &accepted ); + + if ((err == noErr) && !accepted) + { + // user clicked Cancel button + err = kPMCancel; + } + + // If the user did not cancel, flatten and save the PageFormat object + // with our document. + if (err == noErr) + { + result = wxID_OK; + } + + if ((err != noErr) && (err != kPMCancel)) + { + wxString message; + + message.Printf( wxT("Print Error %d"), err ); + wxMessageDialog dialog( NULL, message, wxEmptyString, wxICON_HAND | wxOK ); + dialog.ShowModal(); + } + + if (result == wxID_OK) + { + m_pageSetupData.GetPrintData().ConvertFromNative(); + m_pageSetupData.SetPaperSize( m_pageSetupData.GetPrintData().GetPaperSize() ); + ((wxMacCarbonPrintData*)m_pageSetupData.GetPrintData().GetNativeData())->TransferTo( &m_pageSetupData ); + } +#endif + return result; +} + + +IMPLEMENT_CLASS(wxMacPageMarginsDialog, wxDialog) + +wxMacPageMarginsDialog::wxMacPageMarginsDialog(wxFrame *parent, wxPageSetupData *data) : + wxDialog(parent, wxID_ANY, wxString(wxT("Page Margins"))), + m_pageSetupDialogData(data) + { + GetMinMargins(); + wxBoxSizer *colSizer = new wxBoxSizer(wxVERTICAL); + wxFlexGridSizer *gridSizer = new wxFlexGridSizer(4, 5, 5); + colSizer->Add(gridSizer, wxSizerFlags().Border(wxALL, 5)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, wxT("Left (mm):")), wxSizerFlags().Right()); + gridSizer->Add(m_LeftMargin = new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Left()); + gridSizer->Add(new wxStaticText(this, wxID_ANY, wxT("Top (mm):")), wxSizerFlags().Right()); + gridSizer->Add(m_TopMargin = new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Left()); + gridSizer->Add(new wxStaticText(this, wxID_ANY, wxT("Right (mm):")), wxSizerFlags().Right()); + gridSizer->Add(m_RightMargin = new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Left()); + gridSizer->Add(new wxStaticText(this, wxID_ANY, wxT("Bottom (mm):")), wxSizerFlags().Right()); + gridSizer->Add(m_BottomMargin = new wxTextCtrl(this, wxID_ANY), wxSizerFlags().Left()); + colSizer->Add(new wxStaticLine(this), wxSizerFlags().Expand().Border(wxTOP|wxBOTTOM, 5)); + colSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Expand().Border(wxALL, 5)); + TransferToWindow(); + SetSizerAndFit(colSizer); + Center(wxBOTH); + } + +bool wxMacPageMarginsDialog::TransferToWindow() + { + wxASSERT(m_pageSetupDialogData); + wxPoint topLeft = m_pageSetupDialogData->GetMarginTopLeft(); + wxPoint bottomRight = m_pageSetupDialogData->GetMarginBottomRight(); + wxPoint minTopLeft = m_pageSetupDialogData->GetMinMarginTopLeft(); + wxPoint minBottomRight = m_pageSetupDialogData->GetMinMarginBottomRight(); + m_LeftMargin->SetValue(wxString::Format(wxT("%d"), wxMax(topLeft.x, minTopLeft.x))); + m_LeftMargin->SetSelection(-1, -1); + m_TopMargin->SetValue(wxString::Format(wxT("%d"), wxMax(topLeft.y, minTopLeft.y))); + m_TopMargin->SetSelection(-1, -1); + m_RightMargin->SetValue(wxString::Format(wxT("%d"), wxMax(bottomRight.x, minBottomRight.x))); + m_RightMargin->SetSelection(-1, -1); + m_BottomMargin->SetValue(wxString::Format(wxT("%d"), wxMax(bottomRight.y, minBottomRight.y))); + m_BottomMargin->SetSelection(-1, -1); + m_LeftMargin->SetFocus(); + return true; + } + +bool wxMacPageMarginsDialog::TransferDataFromWindow() + { + wxPoint topLeft, bottomRight; + if (!CheckValue(m_LeftMargin, &topLeft.x, m_MinMarginTopLeft.x, wxT("left margin"))) return false; + if (!CheckValue(m_TopMargin, &topLeft.y, m_MinMarginTopLeft.y, wxT("top margin"))) return false; + if (!CheckValue(m_RightMargin, &bottomRight.x, m_MinMarginBottomRight.x, wxT("right margin"))) return false; + if (!CheckValue(m_BottomMargin, &bottomRight.y, m_MinMarginBottomRight.y, wxT("bottom margin"))) return false; + m_pageSetupDialogData->SetMarginTopLeft(topLeft); + m_pageSetupDialogData->SetMarginBottomRight(bottomRight); + return true; + } + +bool wxMacPageMarginsDialog::CheckValue(wxTextCtrl* textCtrl, int *value, int minValue, const wxString& name) + { + long lvalue; + if (!textCtrl->GetValue().ToLong(&lvalue)) + { + wxMessageBox(wxString::Format(wxT("Sorry, \"%s\" is not a valid numerical value for the %s"), textCtrl->GetValue().c_str(), name.c_str()), wxT("Page Margin Error")); + return false; + } + if (lvalue < minValue) + { + wxMessageBox(wxString::Format(wxT("Sorry, \"%s\" is not a valid value for the %s, which must be >= %d"), textCtrl->GetValue().c_str(), name.c_str(), minValue), wxT("Page Margin Error")); + textCtrl->SetValue(wxString::Format(wxT("%d"), minValue)); + textCtrl->SetSelection(-1, -1); + textCtrl->SetFocus(); + return false; + } + *value = int(lvalue); + return true; + } + +void wxMacPageMarginsDialog::GetMinMargins() + { + m_MinMarginTopLeft = m_pageSetupDialogData->GetMinMarginTopLeft(); + m_MinMarginBottomRight = m_pageSetupDialogData->GetMinMarginBottomRight(); + } + + + +#endif diff --git a/src/osx/carbon/printmac.cpp b/src/osx/carbon/printmac.cpp new file mode 100644 index 0000000000..6c34d746f9 --- /dev/null +++ b/src/osx/carbon/printmac.cpp @@ -0,0 +1,620 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/printwin.cpp +// Purpose: wxMacPrinter framework +// Author: Julian Smart +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Julian Smart +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_PRINTING_ARCHITECTURE + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/utils.h" + #include "wx/dc.h" + #include "wx/app.h" + #include "wx/msgdlg.h" + #include "wx/dcprint.h" + #include "wx/math.h" +#endif + +#include "wx/mac/uma.h" + +#include "wx/mac/printmac.h" +#include "wx/mac/private/print.h" + +#include "wx/printdlg.h" +#include "wx/paper.h" +#include "wx/mac/printdlg.h" + +#include + +IMPLEMENT_DYNAMIC_CLASS(wxMacCarbonPrintData, wxPrintNativeDataBase) +IMPLEMENT_DYNAMIC_CLASS(wxMacPrinter, wxPrinterBase) +IMPLEMENT_CLASS(wxMacPrintPreview, wxPrintPreviewBase) + +bool wxMacCarbonPrintData::IsOk() const +{ + return (m_macPageFormat != kPMNoPageFormat) && (m_macPrintSettings != kPMNoPrintSettings) && (m_macPrintSession != kPMNoReference); +} +wxMacCarbonPrintData::wxMacCarbonPrintData() +{ + m_macPageFormat = kPMNoPageFormat; + m_macPrintSettings = kPMNoPrintSettings; + m_macPrintSession = kPMNoReference ; + ValidateOrCreate() ; +} + +wxMacCarbonPrintData::~wxMacCarbonPrintData() +{ + if (m_macPageFormat != kPMNoPageFormat) + { + (void)PMRelease(m_macPageFormat); + m_macPageFormat = kPMNoPageFormat; + } + + if (m_macPrintSettings != kPMNoPrintSettings) + { + (void)PMRelease(m_macPrintSettings); + m_macPrintSettings = kPMNoPrintSettings; + } + + if ( m_macPrintSession != kPMNoReference ) + { + (void)PMRelease(m_macPrintSession); + m_macPrintSession = kPMNoReference; + } +} + +void wxMacCarbonPrintData::ValidateOrCreate() +{ + OSStatus err = noErr ; + if ( m_macPrintSession == kPMNoReference ) + { + err = PMCreateSession( &m_macPrintSession ) ; + } + // Set up a valid PageFormat object. + if ( m_macPageFormat == kPMNoPageFormat) + { + err = PMCreatePageFormat(&m_macPageFormat); + + // Note that PMPageFormat is not session-specific, but calling + // PMSessionDefaultPageFormat assigns values specific to the printer + // associated with the current printing session. + if ((err == noErr) && + ( m_macPageFormat != kPMNoPageFormat)) + { + err = PMSessionDefaultPageFormat(m_macPrintSession, + m_macPageFormat); + } + } + else + { + err = PMSessionValidatePageFormat(m_macPrintSession, + m_macPageFormat, + kPMDontWantBoolean); + } + + // Set up a valid PrintSettings object. + if ( m_macPrintSettings == kPMNoPrintSettings) + { + err = PMCreatePrintSettings( &m_macPrintSettings); + + // Note that PMPrintSettings is not session-specific, but calling + // PMSessionDefaultPrintSettings assigns values specific to the printer + // associated with the current printing session. + if ((err == noErr) && + ( m_macPrintSettings != kPMNoPrintSettings)) + { + err = PMSessionDefaultPrintSettings(m_macPrintSession, + m_macPrintSettings); + } + } + else + { + err = PMSessionValidatePrintSettings( m_macPrintSession, + m_macPrintSettings, + kPMDontWantBoolean); + } +} + +bool wxMacCarbonPrintData::TransferFrom( const wxPrintData &data ) +{ + ValidateOrCreate() ; + PMSetCopies( (PMPrintSettings) m_macPrintSettings , data.GetNoCopies() , false ) ; + if ( data.IsOrientationReversed() ) + PMSetOrientation( (PMPageFormat) m_macPageFormat , ( data.GetOrientation() == wxLANDSCAPE ) ? + kPMReverseLandscape : kPMReversePortrait , false ) ; + else + PMSetOrientation( (PMPageFormat) m_macPageFormat , ( data.GetOrientation() == wxLANDSCAPE ) ? + kPMLandscape : kPMPortrait , false ) ; + // collate cannot be set +#if 0 // not yet tested + if ( !m_printerName.empty() ) + PMSessionSetCurrentPrinter( (PMPrintSession) m_macPrintSession , wxCFStringRef( m_printerName , wxFont::GetDefaultEncoding() ) ) ; +#endif +#ifndef __LP64__ + PMColorMode color ; + PMGetColorMode( (PMPrintSettings) m_macPrintSettings, &color ) ; + if ( data.GetColour() ) + { + if ( color == kPMBlackAndWhite ) + PMSetColorMode( (PMPrintSettings) m_macPrintSettings, kPMColor ) ; + } + else + PMSetColorMode( (PMPrintSettings) m_macPrintSettings, kPMBlackAndWhite ) ; +#endif + + PMDuplexMode mode = 0 ; + switch( data.GetDuplex() ) + { + case wxDUPLEX_HORIZONTAL : + mode = kPMDuplexNoTumble ; + break ; + case wxDUPLEX_VERTICAL : + mode = kPMDuplexTumble ; + break ; + case wxDUPLEX_SIMPLEX : + default : + mode = kPMDuplexNone ; + break ; + } + PMSetDuplex( (PMPrintSettings) m_macPrintSettings, mode ) ; + + // PMQualityMode not yet accessible via API + // todo paperSize + + PMResolution res; + PMPrinter printer; + PMSessionGetCurrentPrinter(m_macPrintSession, &printer); +#if 0 // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + PMPrinterGetOutputResolution( printer, + (PMPrintSettings) m_macPrintSettings, &res) ; + // TODO transfer ? into page format ? + // may fail ! +#else + PMTag tag = kPMMaxSquareResolution; + PMPrinterGetPrinterResolution(printer, tag, &res); + PMSetResolution((PMPageFormat) m_macPageFormat, &res); +#endif + // after setting the new resolution the format has to be updated, otherwise the page rect remains + // at the 'old' scaling + PMSessionValidatePageFormat((PMPrintSession) m_macPrintSession, + (PMPageFormat) m_macPageFormat, + kPMDontWantBoolean) ; + + return true ; +} + +bool wxMacCarbonPrintData::TransferTo( wxPrintData &data ) +{ + OSStatus err = noErr ; + + UInt32 copies ; + err = PMGetCopies( m_macPrintSettings , &copies ) ; + if ( err == noErr ) + data.SetNoCopies( copies ) ; + + PMOrientation orientation ; + err = PMGetOrientation( m_macPageFormat , &orientation ) ; + if ( err == noErr ) + { + if ( orientation == kPMPortrait || orientation == kPMReversePortrait ) + { + data.SetOrientation( wxPORTRAIT ); + data.SetOrientationReversed( orientation == kPMReversePortrait ); + } + else + { + data.SetOrientation( wxLANDSCAPE ); + data.SetOrientationReversed( orientation == kPMReverseLandscape ); + } + } + + // collate cannot be set +#if 0 + { + wxCFStringRef name ; + PMPrinter printer ; + PMSessionGetCurrentPrinter( m_macPrintSession , + &printer ) ; + m_printerName = name.AsString() ; + } +#endif + +#ifndef __LP64__ + PMColorMode color ; + err = PMGetColorMode( m_macPrintSettings, &color ) ; + if ( err == noErr ) + data.SetColour( !(color == kPMBlackAndWhite) ) ; +#endif + PMDuplexMode mode = 0 ; + PMGetDuplex( (PMPrintSettings) m_macPrintSettings, &mode ) ; + switch( mode ) + { + case kPMDuplexNoTumble : + data.SetDuplex(wxDUPLEX_HORIZONTAL); + break ; + case kPMDuplexTumble : + data.SetDuplex(wxDUPLEX_VERTICAL); + break ; + case kPMDuplexNone : + default : + data.SetDuplex(wxDUPLEX_SIMPLEX); + break ; + } + // PMQualityMode not yet accessible via API + + PMPaper paper ; + PMGetPageFormatPaper( m_macPageFormat, &paper ); + + PMRect rPaper; + err = PMGetUnadjustedPaperRect( m_macPageFormat, &rPaper); + if ( err == noErr ) + { + wxSize sz((int)(( rPaper.right - rPaper.left ) * pt2mm + 0.5 ) , + (int)(( rPaper.bottom - rPaper.top ) * pt2mm + 0.5 )); + data.SetPaperSize(sz); + wxPaperSize id = wxThePrintPaperDatabase->GetSize(wxSize(sz.x* 10, sz.y * 10)); + if (id != wxPAPER_NONE) + { + data.SetPaperId(id); + } + } + return true ; +} + +void wxMacCarbonPrintData::TransferFrom( wxPageSetupData *WXUNUSED(data) ) +{ + // should we setup the page rect here ? + // since MacOS sometimes has two same paper rects with different + // page rects we could make it roundtrip safe perhaps +} + +void wxMacCarbonPrintData::TransferTo( wxPageSetupData* data ) +{ + PMRect rPaper; + OSStatus err = PMGetUnadjustedPaperRect(m_macPageFormat, &rPaper); + if ( err == noErr ) + { + wxSize sz((int)(( rPaper.right - rPaper.left ) * pt2mm + 0.5 ) , + (int)(( rPaper.bottom - rPaper.top ) * pt2mm + 0.5 )); + data->SetPaperSize(sz); + + PMRect rPage ; + err = PMGetUnadjustedPageRect(m_macPageFormat , &rPage ) ; + if ( err == noErr ) + { + data->SetMinMarginTopLeft( wxPoint ( + (int)(((double) rPage.left - rPaper.left ) * pt2mm) , + (int)(((double) rPage.top - rPaper.top ) * pt2mm) ) ) ; + + data->SetMinMarginBottomRight( wxPoint ( + (wxCoord)(((double) rPaper.right - rPage.right ) * pt2mm), + (wxCoord)(((double) rPaper.bottom - rPage.bottom ) * pt2mm)) ) ; + + if ( data->GetMarginTopLeft().x < data->GetMinMarginTopLeft().x ) + data->SetMarginTopLeft( wxPoint( data->GetMinMarginTopLeft().x , + data->GetMarginTopLeft().y ) ) ; + + if ( data->GetMarginBottomRight().x < data->GetMinMarginBottomRight().x ) + data->SetMarginBottomRight( wxPoint( data->GetMinMarginBottomRight().x , + data->GetMarginBottomRight().y ) ); + + if ( data->GetMarginTopLeft().y < data->GetMinMarginTopLeft().y ) + data->SetMarginTopLeft( wxPoint( data->GetMarginTopLeft().x , data->GetMinMarginTopLeft().y ) ); + + if ( data->GetMarginBottomRight().y < data->GetMinMarginBottomRight().y ) + data->SetMarginBottomRight( wxPoint( data->GetMarginBottomRight().x , + data->GetMinMarginBottomRight().y) ); + } + } +} + +void wxMacCarbonPrintData::TransferTo( wxPrintDialogData* data ) +{ + UInt32 minPage , maxPage ; + PMGetPageRange( m_macPrintSettings , &minPage , &maxPage ) ; + data->SetMinPage( minPage ) ; + data->SetMaxPage( maxPage ) ; + UInt32 copies ; + PMGetCopies( m_macPrintSettings , &copies ) ; + data->SetNoCopies( copies ) ; + UInt32 from , to ; + PMGetFirstPage( m_macPrintSettings , &from ) ; + PMGetLastPage( m_macPrintSettings , &to ) ; + if ( to >= 0x7FFFFFFF ) // due to an OS Bug we don't get back kPMPrintAllPages + { + data->SetAllPages( true ) ; + // This means all pages, more or less + data->SetFromPage(1); + data->SetToPage(32000); + } + else + { + data->SetFromPage( from ) ; + data->SetToPage( to ) ; + data->SetAllPages( false ); + } +} + +void wxMacCarbonPrintData::TransferFrom( wxPrintDialogData* data ) +{ + // Respect the value of m_printAllPages + if ( data->GetAllPages() ) + PMSetPageRange( m_macPrintSettings , data->GetMinPage() , (UInt32) kPMPrintAllPages ) ; + else + PMSetPageRange( m_macPrintSettings , data->GetMinPage() , data->GetMaxPage() ) ; + PMSetCopies( m_macPrintSettings , data->GetNoCopies() , false ) ; + PMSetFirstPage( m_macPrintSettings , data->GetFromPage() , false ) ; + + if (data->GetAllPages() || data->GetFromPage() == 0) + PMSetLastPage( m_macPrintSettings , (UInt32) kPMPrintAllPages, true ) ; + else + PMSetLastPage( m_macPrintSettings , (UInt32) data->GetToPage() , false ) ; +} + +/* +* Printer +*/ + +wxMacPrinter::wxMacPrinter(wxPrintDialogData *data): +wxPrinterBase(data) +{ +} + +wxMacPrinter::~wxMacPrinter(void) +{ +} + +bool wxMacPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt) +{ + sm_abortIt = false; + sm_abortWindow = NULL; + + if (!printout) + return false; + + printout->SetIsPreview(false); + if (m_printDialogData.GetMinPage() < 1) + m_printDialogData.SetMinPage(1); + if (m_printDialogData.GetMaxPage() < 1) + m_printDialogData.SetMaxPage(9999); + + // Create a suitable device context + wxPrinterDC *dc = NULL; + if (prompt) + { + wxMacPrintDialog dialog(parent, & m_printDialogData); + if (dialog.ShowModal() == wxID_OK) + { + dc = wxDynamicCast(dialog.GetPrintDC(), wxPrinterDC); + wxASSERT(dc); + m_printDialogData = dialog.GetPrintDialogData(); + } + } + else + { + dc = new wxPrinterDC( m_printDialogData.GetPrintData() ) ; + } + + // May have pressed cancel. + if (!dc || !dc->IsOk()) + { + if (dc) + delete dc; + return false; + } + + // on the mac we have always pixels as addressing mode with 72 dpi + printout->SetPPIScreen(72, 72); + PMResolution res; + wxMacCarbonPrintData* nativeData = (wxMacCarbonPrintData*) + (m_printDialogData.GetPrintData().GetNativeData()); +#if 0 // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + PMPrinter printer; + PMSessionGetCurrentPrinter(nativeData->m_macPrintSession, &printer); + PMPrinterGetOutputResolution( printer, nativeData->m_macPrintSettings, &res) ; +#else + PMGetResolution((PMPageFormat) (nativeData->m_macPageFormat), &res); +#endif + printout->SetPPIPrinter(int(res.hRes), int(res.vRes)); + + // Set printout parameters + printout->SetDC(dc); + + int w, h; + dc->GetSize(&w, &h); + printout->SetPageSizePixels((int)w, (int)h); + printout->SetPaperRectPixels(dc->GetPaperRect()); + wxCoord mw, mh; + dc->GetSizeMM(&mw, &mh); + printout->SetPageSizeMM((int)mw, (int)mh); + + // Create an abort window + wxBeginBusyCursor(); + + printout->OnPreparePrinting(); + + // Get some parameters from the printout, if defined + int fromPage, toPage; + int minPage, maxPage; + printout->GetPageInfo(&minPage, &maxPage, &fromPage, &toPage); + + if (maxPage == 0) + { + wxEndBusyCursor(); + return false; + } + + // Only set min and max, because from and to have been + // set by the user + m_printDialogData.SetMinPage(minPage); + m_printDialogData.SetMaxPage(maxPage); + + printout->OnBeginPrinting(); + + bool keepGoing = true; + + if (!printout->OnBeginDocument(m_printDialogData.GetFromPage(), m_printDialogData.GetToPage())) + { + wxEndBusyCursor(); + wxMessageBox(wxT("Could not start printing."), wxT("Print Error"), wxOK, parent); + } + + int pn; + for (pn = m_printDialogData.GetFromPage(); + keepGoing && (pn <= m_printDialogData.GetToPage()) && printout->HasPage(pn); + pn++) + { + if (sm_abortIt) + { + keepGoing = false; + break; + } + else + { + dc->StartPage(); + keepGoing = printout->OnPrintPage(pn); + dc->EndPage(); + } + } + printout->OnEndDocument(); + + printout->OnEndPrinting(); + + if (sm_abortWindow) + { + sm_abortWindow->Show(false); + delete sm_abortWindow; + sm_abortWindow = NULL; + } + + wxEndBusyCursor(); + + delete dc; + + return true; +} + +wxDC* wxMacPrinter::PrintDialog(wxWindow *parent) +{ + wxDC* dc = (wxDC*) NULL; + + wxPrintDialog dialog(parent, & m_printDialogData); + int ret = dialog.ShowModal(); + + if (ret == wxID_OK) + { + dc = dialog.GetPrintDC(); + m_printDialogData = dialog.GetPrintDialogData(); + } + + return dc; +} + +bool wxMacPrinter::Setup(wxWindow *WXUNUSED(parent)) +{ +#if 0 + wxPrintDialog dialog(parent, & m_printDialogData); + dialog.GetPrintDialogData().SetSetupDialog(true); + + int ret = dialog.ShowModal(); + + if (ret == wxID_OK) + m_printDialogData = dialog.GetPrintDialogData(); + + return (ret == wxID_OK); +#endif + + return wxID_CANCEL; +} + +/* +* Print preview +*/ + +wxMacPrintPreview::wxMacPrintPreview(wxPrintout *printout, + wxPrintout *printoutForPrinting, + wxPrintDialogData *data) + : wxPrintPreviewBase(printout, printoutForPrinting, data) +{ + DetermineScaling(); +} + +wxMacPrintPreview::wxMacPrintPreview(wxPrintout *printout, wxPrintout *printoutForPrinting, wxPrintData *data): +wxPrintPreviewBase(printout, printoutForPrinting, data) +{ + DetermineScaling(); +} + +wxMacPrintPreview::~wxMacPrintPreview(void) +{ +} + +bool wxMacPrintPreview::Print(bool interactive) +{ + if (!m_printPrintout) + return false; + + wxMacPrinter printer(&m_printDialogData); + return printer.Print(m_previewFrame, m_printPrintout, interactive); +} + +void wxMacPrintPreview::DetermineScaling(void) +{ + int screenWidth , screenHeight ; + wxDisplaySize( &screenWidth , &screenHeight ) ; + + wxSize ppiScreen( 72 , 72 ) ; + wxSize ppiPrinter( 72 , 72 ) ; + + // Note that with Leopard, screen dpi=72 is no longer a given + m_previewPrintout->SetPPIScreen( ppiScreen.x , ppiScreen.y ) ; + + wxCoord w , h ; + wxCoord ww, hh; + wxRect paperRect; + + // Get a device context for the currently selected printer + wxPrinterDC printerDC(m_printDialogData.GetPrintData()); + if (printerDC.IsOk()) + { + printerDC.GetSizeMM(&ww, &hh); + printerDC.GetSize( &w , &h ) ; + ppiPrinter = printerDC.GetPPI() ; + paperRect = printerDC.GetPaperRect(); + m_isOk = true ; + } + else + { + // use some defaults + w = 8 * 72 ; + h = 11 * 72 ; + ww = (wxCoord) (w * 25.4 / ppiPrinter.x) ; + hh = (wxCoord) (h * 25.4 / ppiPrinter.y) ; + paperRect = wxRect(0, 0, w, h); + m_isOk = false ; + } + m_pageWidth = w; + m_pageHeight = h; + + m_previewPrintout->SetPageSizePixels(w , h) ; + m_previewPrintout->SetPageSizeMM(ww, hh); + m_previewPrintout->SetPaperRectPixels(paperRect); + m_previewPrintout->SetPPIPrinter( ppiPrinter.x , ppiPrinter.y ) ; + + m_previewScaleX = float(ppiScreen.x) / ppiPrinter.x; + m_previewScaleY = float(ppiScreen.y) / ppiPrinter.y; +} + +#endif diff --git a/src/osx/carbon/radiobox.cpp b/src/osx/carbon/radiobox.cpp new file mode 100644 index 0000000000..69ce082c1b --- /dev/null +++ b/src/osx/carbon/radiobox.cpp @@ -0,0 +1,527 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/radiobox.cpp +// Purpose: wxRadioBox +// Author: Stefan Csomor +// Modified by: JS Lair (99/11/15) first implementation +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_RADIOBOX + +#include "wx/radiobox.h" + +#ifndef WX_PRECOMP + #include "wx/radiobut.h" + #include "wx/arrstr.h" +#endif + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl) + + +BEGIN_EVENT_TABLE(wxRadioBox, wxControl) + EVT_RADIOBUTTON( wxID_ANY , wxRadioBox::OnRadioButton ) +END_EVENT_TABLE() + + +void wxRadioBox::OnRadioButton( wxCommandEvent &outer ) +{ + if ( outer.IsChecked() ) + { + wxCommandEvent event( wxEVT_COMMAND_RADIOBOX_SELECTED, m_windowId ); + int i = GetSelection() ; + event.SetInt(i); + event.SetString(GetString(i)); + event.SetEventObject( this ); + ProcessCommand(event); + } +} + +wxRadioBox::wxRadioBox() +{ + m_noItems = 0; + m_noRowsOrCols = 0; + m_radioButtonCycle = NULL; +} + +wxRadioBox::~wxRadioBox() +{ + m_isBeingDeleted = true; + + wxRadioButton *next, *current; + + current = m_radioButtonCycle->NextInCycle(); + if (current != NULL) + { + while (current != m_radioButtonCycle) + { + next = current->NextInCycle(); + delete current; + + current = next; + } + + delete current; + } +} + +// Create the radiobox for two-step construction + +bool wxRadioBox::Create( wxWindow *parent, + wxWindowID id, const wxString& label, + const wxPoint& pos, const wxSize& size, + const wxArrayString& choices, + int majorDim, long style, + const wxValidator& val, const wxString& name ) +{ + wxCArrayString chs(choices); + + return Create( + parent, id, label, pos, size, chs.GetCount(), + chs.GetStrings(), majorDim, style, val, name); +} + +bool wxRadioBox::Create( wxWindow *parent, + wxWindowID id, const wxString& label, + const wxPoint& pos, const wxSize& size, + int n, const wxString choices[], + int majorDim, long style, + const wxValidator& val, const wxString& name ) +{ + m_macIsUserPane = false ; + + if ( !wxControl::Create( parent, id, pos, size, style, val, name ) ) + return false; + + int i; + + m_noItems = (unsigned int)n; + m_noRowsOrCols = majorDim; + m_radioButtonCycle = NULL; + + SetMajorDim( majorDim == 0 ? n : majorDim, style ); + + m_labelOrig = m_label = label; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + if ( bounds.right <= bounds.left ) + bounds.right = bounds.left + 100; + if ( bounds.bottom <= bounds.top ) + bounds.bottom = bounds.top + 100; + + m_peer = new wxMacControl( this ); + + OSStatus err = CreateGroupBoxControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), + &bounds, CFSTR("") , true /*primary*/, + m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + for (i = 0; i < n; i++) + { + wxRadioButton *radBtn = new wxRadioButton( + this, + wxID_ANY, + GetLabelText(choices[i]), + wxPoint( 5, 20 * i + 10 ), + wxDefaultSize, + i == 0 ? wxRB_GROUP : 0 ); + + if ( i == 0 ) + m_radioButtonCycle = radBtn; +// m_radioButtonCycle = radBtn->AddInCycle( m_radioButtonCycle ); + } + + SetSelection( 0 ); + MacPostControlCreate( pos, size ); + + return true; +} + +// Enables or disables the entire radiobox +// +bool wxRadioBox::Enable(bool enable) +{ + wxRadioButton *current; + + if (!wxControl::Enable( enable )) + return false; + + current = m_radioButtonCycle; + for (unsigned int i = 0; i < m_noItems; i++) + { + current->Enable( enable ); + current = current->NextInCycle(); + } + + return true; +} + +// Enables or disables an given button +// +bool wxRadioBox::Enable(unsigned int item, bool enable) +{ + if (!IsValid( item )) + return false; + + unsigned int i = 0; + wxRadioButton *current = m_radioButtonCycle; + while (i != item) + { + i++; + current = current->NextInCycle(); + } + + return current->Enable( enable ); +} + +bool wxRadioBox::IsItemEnabled(unsigned int item) const +{ + if (!IsValid( item )) + return false; + + unsigned int i = 0; + wxRadioButton *current = m_radioButtonCycle; + while (i != item) + { + i++; + current = current->NextInCycle(); + } + + return current->IsEnabled(); +} + +// Returns the radiobox label +// +wxString wxRadioBox::GetLabel() const +{ + return wxControl::GetLabel(); +} + +// Returns the label for the given button +// +wxString wxRadioBox::GetString(unsigned int item) const +{ + wxRadioButton *current; + + if (!IsValid( item )) + return wxEmptyString; + + unsigned int i = 0; + current = m_radioButtonCycle; + while (i != item) + { + i++; + current = current->NextInCycle(); + } + + return current->GetLabel(); +} + +// Returns the zero-based position of the selected button +// +int wxRadioBox::GetSelection() const +{ + int i; + wxRadioButton *current; + + i = 0; + current = m_radioButtonCycle; + while (!current->GetValue()) + { + i++; + current = current->NextInCycle(); + } + + return i; +} + +// Sets the radiobox label +// +void wxRadioBox::SetLabel(const wxString& label) +{ + return wxControl::SetLabel( label ); +} + +// Sets the label of a given button +// +void wxRadioBox::SetString(unsigned int item,const wxString& label) +{ + if (!IsValid( item )) + return; + + unsigned int i = 0; + wxRadioButton *current = m_radioButtonCycle; + while (i != item) + { + i++; + current = current->NextInCycle(); + } + + return current->SetLabel( label ); +} + +// Sets a button by passing the desired position. This does not cause +// wxEVT_COMMAND_RADIOBOX_SELECTED event to get emitted +// +void wxRadioBox::SetSelection(int item) +{ + int i; + wxRadioButton *current; + + if (!IsValid( item )) + return; + + i = 0; + current = m_radioButtonCycle; + while (i != item) + { + i++; + current = current->NextInCycle(); + } + + current->SetValue( true ); +} + +// Shows or hides the entire radiobox +// +bool wxRadioBox::Show(bool show) +{ + wxRadioButton *current; + + current = m_radioButtonCycle; + for (unsigned int i=0; iShow( show ); + current = current->NextInCycle(); + } + + wxControl::Show( show ); + + return true; +} + +// Shows or hides the given button +// +bool wxRadioBox::Show(unsigned int item, bool show) +{ + if (!IsValid( item )) + return false; + + unsigned int i = 0; + wxRadioButton *current = m_radioButtonCycle; + while (i != item) + { + i++; + current = current->NextInCycle(); + } + + return current->Show( show ); +} + +bool wxRadioBox::IsItemShown(unsigned int item) const +{ + if (!IsValid( item )) + return false; + + unsigned int i = 0; + wxRadioButton *current = m_radioButtonCycle; + while (i != item) + { + i++; + current = current->NextInCycle(); + } + + return current->IsShown(); +} + + +// Simulates the effect of the user issuing a command to the item +// +void wxRadioBox::Command( wxCommandEvent& event ) +{ + SetSelection( event.GetInt() ); + ProcessCommand( event ); +} + +// Sets the selected button to receive keyboard input +// +void wxRadioBox::SetFocus() +{ + wxRadioButton *current; + + current = m_radioButtonCycle; + while (!current->GetValue()) + { + current = current->NextInCycle(); + } + + current->SetFocus(); +} + +// Simulates the effect of the user issuing a command to the item +// +#define RADIO_SIZE 20 + +void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags) +{ + int i; + wxRadioButton *current; + + // define the position + + int x_current, y_current; + int x_offset, y_offset; + int widthOld, heightOld; + + GetSize( &widthOld, &heightOld ); + GetPosition( &x_current, &y_current ); + + x_offset = x; + y_offset = y; + if (!(sizeFlags & wxSIZE_ALLOW_MINUS_ONE)) + { + if (x == wxDefaultCoord) + x_offset = x_current; + if (y == wxDefaultCoord) + y_offset = y_current; + } + + // define size + int charWidth, charHeight; + int maxWidth, maxHeight; + int eachWidth[128], eachHeight[128]; + int totWidth, totHeight; + + GetTextExtent( + wxT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), + &charWidth, &charHeight ); + + charWidth /= 52; + + maxWidth = -1; + maxHeight = -1; + for (unsigned int i = 0 ; i < m_noItems; i++) + { + GetTextExtent(GetString(i), &eachWidth[i], &eachHeight[i] ); + eachWidth[i] = (int)(eachWidth[i] + RADIO_SIZE); + eachHeight[i] = (int)((3 * eachHeight[i]) / 2); + + if (maxWidth < eachWidth[i]) + maxWidth = eachWidth[i]; + if (maxHeight < eachHeight[i]) + maxHeight = eachHeight[i]; + } + + totHeight = GetRowCount() * maxHeight; + totWidth = GetColumnCount() * (maxWidth + charWidth); + + wxSize sz = DoGetSizeFromClientSize( wxSize( totWidth, totHeight ) ) ; + + // change the width / height only when specified + if ( width == wxDefaultCoord ) + { + if ( sizeFlags & wxSIZE_AUTO_WIDTH ) + width = sz.x; + else + width = widthOld; + } + + if ( height == wxDefaultCoord ) + { + if ( sizeFlags & wxSIZE_AUTO_HEIGHT ) + height = sz.y; + else + height = heightOld; + } + + wxControl::DoSetSize( x_offset, y_offset, width, height, wxSIZE_AUTO ); + + // arrange radio buttons + int x_start, y_start; + + x_start = 0; + y_start = 0; + + x_offset = x_start; + y_offset = y_start; + + current = m_radioButtonCycle; + for (i = 0 ; i < (int)m_noItems; i++) + { + // not to do for the zero button! + if ((i > 0) && ((i % GetMajorDim()) == 0)) + { + if (m_windowStyle & wxRA_SPECIFY_ROWS) + { + x_offset += maxWidth + charWidth; + y_offset = y_start; + } + else + { + x_offset = x_start; + y_offset += maxHeight ; //+ charHeight / 2 + } + } + + current->SetSize( x_offset, y_offset, eachWidth[i], eachHeight[i]); + current = current->NextInCycle(); + + if (m_windowStyle & wxRA_SPECIFY_ROWS) + y_offset += maxHeight ; // + charHeight / 2 + else + x_offset += maxWidth + charWidth; + } +} + +wxSize wxRadioBox::DoGetBestSize() const +{ + int charWidth, charHeight; + int maxWidth, maxHeight; + int eachWidth, eachHeight; + int totWidth, totHeight; + + wxFont font = GetFont(); // GetParent()->GetFont() + GetTextExtent( + wxT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), + &charWidth, &charHeight, NULL, NULL, &font ); + + charWidth /= 52; + + maxWidth = -1; + maxHeight = -1; + + for (unsigned int i = 0 ; i < m_noItems; i++) + { + GetTextExtent(GetString(i), &eachWidth, &eachHeight, NULL, NULL, &font ); + eachWidth = (int)(eachWidth + RADIO_SIZE); + eachHeight = (int)((3 * eachHeight) / 2); + if (maxWidth < eachWidth) + maxWidth = eachWidth; + if (maxHeight < eachHeight) + maxHeight = eachHeight; + } + + totHeight = GetRowCount() * maxHeight; + totWidth = GetColumnCount() * (maxWidth + charWidth); + + wxSize sz = DoGetSizeFromClientSize( wxSize( totWidth, totHeight ) ); + totWidth = sz.x; + totHeight = sz.y; + + // handle radio box title as well + GetTextExtent( GetLabel(), &eachWidth, NULL ); + eachWidth = (int)(eachWidth + RADIO_SIZE) + 3 * charWidth; + if (totWidth < eachWidth) + totWidth = eachWidth; + + return wxSize( totWidth, totHeight ); +} + +#endif // wxUSE_RADIOBOX diff --git a/src/osx/carbon/radiobut.cpp b/src/osx/carbon/radiobut.cpp new file mode 100644 index 0000000000..7f866d2536 --- /dev/null +++ b/src/osx/carbon/radiobut.cpp @@ -0,0 +1,178 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: radiobut.cpp +// Purpose: wxRadioButton +// Author: AUTHOR +// Modified by: JS Lair (99/11/15) adding the cyclic group notion for radiobox +// Created: ??/??/98 +// RCS-ID: $Id$ +// Copyright: (c) AUTHOR +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_RADIOBTN + +#include "wx/radiobut.h" +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl) + + +bool wxRadioButton::Create( wxWindow *parent, + wxWindowID id, + const wxString& label, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_macIsUserPane = false; + + if ( !wxControl::Create( parent, id, pos, size, style, validator, name ) ) + return false; + + m_labelOrig = m_label = label; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + + m_peer = new wxMacControl( this ); + OSStatus err = CreateRadioButtonControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, CFSTR(""), + 0, false /* no autotoggle */, m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + MacPostControlCreate( pos, size ); + + m_cycle = this; + + if (HasFlag( wxRB_GROUP )) + { + AddInCycle( NULL ); + } + else + { + // search backward for last group start + wxRadioButton *chief = NULL; + wxWindowList::compatibility_iterator node = parent->GetChildren().GetLast(); + while (node) + { + wxWindow *child = node->GetData(); + if (child->IsKindOf( CLASSINFO( wxRadioButton ) )) + { + chief = (wxRadioButton*)child; + if (child->HasFlag( wxRB_GROUP )) + break; + } + + node = node->GetPrevious(); + } + + AddInCycle( chief ); + } + + return true; +} + +wxRadioButton::~wxRadioButton() +{ + RemoveFromCycle(); +} + +void wxRadioButton::SetValue(bool val) +{ + wxRadioButton *cycle; + if (m_peer->GetValue() == val) + return; + + m_peer->SetValue( val ); + if (val) + { + cycle = this->NextInCycle(); + if (cycle != NULL) + { + while (cycle != this) + { + cycle->SetValue( false ); + cycle = cycle->NextInCycle(); + } + } + } +} + +bool wxRadioButton::GetValue() const +{ + return m_peer->GetValue(); +} + +void wxRadioButton::Command(wxCommandEvent& event) +{ + SetValue( (event.GetInt() != 0) ); + ProcessCommand( event ); +} + +wxInt32 wxRadioButton::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler), WXEVENTREF WXUNUSED(event) ) +{ + // if already set -> no action + if (GetValue()) + return noErr; + + wxRadioButton *cycle; + cycle = this->NextInCycle(); + if (cycle != NULL) + { + while (cycle != this) + { + if (cycle->GetValue()) + cycle->SetValue( false ); + + cycle = cycle->NextInCycle(); + } + } + + SetValue( true ); + + wxCommandEvent event2( wxEVT_COMMAND_RADIOBUTTON_SELECTED, m_windowId ); + event2.SetEventObject( this ); + event2.SetInt( true ); + ProcessCommand( event2 ); + + return noErr; +} + +wxRadioButton *wxRadioButton::AddInCycle(wxRadioButton *cycle) +{ + wxRadioButton *current; + + if (cycle == NULL) + { + m_cycle = this; + } + else + { + current = cycle; + while (current->m_cycle != cycle) + current = current->m_cycle; + + m_cycle = cycle; + current->m_cycle = this; + } + + return m_cycle; +} + +void wxRadioButton::RemoveFromCycle() +{ + if ((m_cycle == NULL) || (m_cycle == this)) + return; + + // Find the previous one and make it point to the next one + wxRadioButton* prev = this; + while (prev->m_cycle != this) + prev = prev->m_cycle; + + prev->m_cycle = m_cycle; +} + +#endif diff --git a/src/osx/carbon/region.cpp b/src/osx/carbon/region.cpp new file mode 100644 index 0000000000..d08fd4984c --- /dev/null +++ b/src/osx/carbon/region.cpp @@ -0,0 +1,550 @@ +///////////////////////////////////////////////////////////////////////////// +// File: src/mac/carbon/region.cpp +// Purpose: Region class +// Author: Stefan Csomor +// Created: Fri Oct 24 10:46:34 MET 1997 +// RCS-ID: $Id$ +// Copyright: (c) 1997 Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/region.h" + +#ifndef WX_PRECOMP + #include "wx/gdicmn.h" +#endif + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject) +IMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject) + +//----------------------------------------------------------------------------- +// wxRegionRefData implementation +//----------------------------------------------------------------------------- + +class WXDLLEXPORT wxRegionRefData : public wxGDIRefData +{ +public: + wxRegionRefData() + { + m_macRgn.reset( HIShapeCreateMutable() ); + } + + wxRegionRefData(wxCFRef ®ion) + { + m_macRgn.reset( HIShapeCreateMutableCopy(region) ); + } + + wxRegionRefData(long x, long y, long w, long h) + { + CGRect r = CGRectMake(x,y,w,h); + wxCFRef rect(HIShapeCreateWithRect(&r)); + m_macRgn.reset( HIShapeCreateMutableCopy(rect) ); + } + + wxRegionRefData(const wxRegionRefData& data) + : wxGDIRefData() + { + m_macRgn.reset( HIShapeCreateMutableCopy(data.m_macRgn) ); + } + + virtual ~wxRegionRefData() + { + } + + wxCFRef m_macRgn; +}; + +#define M_REGION (((wxRegionRefData*)m_refData)->m_macRgn) +#define OTHER_M_REGION(a) (((wxRegionRefData*)(a.m_refData))->m_macRgn) + +//----------------------------------------------------------------------------- +// wxRegion +//----------------------------------------------------------------------------- + +/*! + * Create an empty region. + */ +wxRegion::wxRegion() +{ + m_refData = new wxRegionRefData(); +} + +wxRegion::wxRegion(WXHRGN hRegion ) +{ + wxCFRef< HIShapeRef > shape( (HIShapeRef) hRegion ); + m_refData = new wxRegionRefData(shape); +} + +wxRegion::wxRegion(long x, long y, long w, long h) +{ + m_refData = new wxRegionRefData(x , y , w , h ); +} + +wxRegion::wxRegion(const wxPoint& topLeft, const wxPoint& bottomRight) +{ + m_refData = new wxRegionRefData(topLeft.x , topLeft.y , + topLeft.x - bottomRight.x , + topLeft.y - bottomRight.y); +} + +wxRegion::wxRegion(const wxRect& rect) +{ + m_refData = new wxRegionRefData(rect.x , rect.y , rect.width , rect.height); +} + +wxRegion::wxRegion(size_t n, const wxPoint *points, int WXUNUSED(fillStyle)) +{ + wxUnusedVar(n); + wxUnusedVar(points); + +#ifndef __LP64__ + + // TODO : any APIs ? + // OS X somehow does not collect the region invisibly as before, so sometimes things + // get drawn on screen instead of just being combined into a region, therefore we allocate a temp gworld now + + GWorldPtr gWorld = NULL; + GWorldPtr oldWorld; + GDHandle oldGDHandle; + OSStatus err; + Rect destRect = { 0, 0, 1, 1 }; + + ::GetGWorld( &oldWorld, &oldGDHandle ); + err = ::NewGWorld( &gWorld, 32, &destRect, NULL, NULL, 0 ); + if ( err == noErr ) + { + ::SetGWorld( gWorld, GetGDevice() ); + + OpenRgn(); + + wxCoord x1, x2 , y1 , y2 ; + x2 = x1 = points[0].x ; + y2 = y1 = points[0].y ; + + ::MoveTo( x1, y1 ); + for (size_t i = 1; i < n; i++) + { + x2 = points[i].x ; + y2 = points[i].y ; + ::LineTo( x2, y2 ); + } + + // close the polyline if necessary + if ( x1 != x2 || y1 != y2 ) + ::LineTo( x1, y1 ) ; + + RgnHandle tempRgn = NewRgn(); + CloseRgn( tempRgn ) ; + + ::SetGWorld( oldWorld, oldGDHandle ); + wxCFRef tempShape( HIShapeCreateWithQDRgn(tempRgn ) ); + m_refData = new wxRegionRefData(tempShape); + DisposeRgn( tempRgn ); + } + else + { + m_refData = new wxRegionRefData; + } +#else + wxFAIL_MSG( "not implemented" ); + m_refData = NULL; +#endif +} + +wxRegion::~wxRegion() +{ + // m_refData unrefed in ~wxObject +} + +wxGDIRefData *wxRegion::CreateGDIRefData() const +{ + return new wxRegionRefData; +} + +wxGDIRefData *wxRegion::CloneGDIRefData(const wxGDIRefData *data) const +{ + return new wxRegionRefData(*wx_static_cast(const wxRegionRefData *, data)); +} + +//----------------------------------------------------------------------------- +//# Modify region +//----------------------------------------------------------------------------- + +//! Clear current region +void wxRegion::Clear() +{ + UnRef(); +} + +// Move the region +bool wxRegion::DoOffset(wxCoord x, wxCoord y) +{ + wxCHECK_MSG( M_REGION, false, _T("invalid wxRegion") ); + + if ( !x && !y ) + // nothing to do + return true; + + verify_noerr( HIShapeOffset( M_REGION , x , y ) ) ; + + return true ; +} + + +//! Union /e region with this. +bool wxRegion::DoCombine(const wxRegion& region, wxRegionOp op) +{ + wxCHECK_MSG( region.Ok(), false, _T("invalid wxRegion") ); + + // Don't change shared data + if (!m_refData) + { + m_refData = new wxRegionRefData(); + } + else if (m_refData->GetRefCount() > 1) + { + wxRegionRefData* ref = (wxRegionRefData*)m_refData; + UnRef(); + m_refData = new wxRegionRefData(*ref); + } + + switch (op) + { + case wxRGN_AND: + verify_noerr( HIShapeIntersect( M_REGION , OTHER_M_REGION(region) , M_REGION ) ); + break ; + + case wxRGN_OR: + verify_noerr( HIShapeUnion( M_REGION , OTHER_M_REGION(region) , M_REGION ) ); + break ; + + case wxRGN_XOR: + { + // XOR is defined as the difference between union and intersection + wxCFRef< HIShapeRef > unionshape( HIShapeCreateUnion( M_REGION , OTHER_M_REGION(region) ) ); + wxCFRef< HIShapeRef > intersectionshape( HIShapeCreateIntersection( M_REGION , OTHER_M_REGION(region) ) ); + verify_noerr( HIShapeDifference( unionshape, intersectionshape, M_REGION ) ); + } + break ; + + case wxRGN_DIFF: + verify_noerr( HIShapeDifference( M_REGION , OTHER_M_REGION(region) , M_REGION ) ) ; + break ; + + case wxRGN_COPY: + default: + M_REGION.reset( HIShapeCreateMutableCopy( OTHER_M_REGION(region) ) ); + break ; + } + + return true; +} + +//----------------------------------------------------------------------------- +//# Information on region +//----------------------------------------------------------------------------- + +bool wxRegion::DoIsEqual(const wxRegion& WXUNUSED(region)) const +{ + wxFAIL_MSG( _T("not implemented") ); + + return false; +} + +// Outer bounds of region +bool wxRegion::DoGetBox(wxCoord& x, wxCoord& y, wxCoord& w, wxCoord& h) const +{ + if (m_refData) + { + CGRect box ; + HIShapeGetBounds( M_REGION , &box ) ; + x = wx_static_cast(int, box.origin.x); + y = wx_static_cast(int, box.origin.y); + w = wx_static_cast(int, box.size.width); + h = wx_static_cast(int, box.size.height); + + return true; + } + else + { + x = y = w = h = 0; + + return false; + } +} + +// Is region empty? +bool wxRegion::IsEmpty() const +{ + if ( m_refData ) + return HIShapeIsEmpty( M_REGION ) ; + else + return true ; +} + +const WXHRGN wxRegion::GetWXHRGN() const +{ + return M_REGION ; +} + +//----------------------------------------------------------------------------- +//# Tests +//----------------------------------------------------------------------------- + +// Does the region contain the point? +wxRegionContain wxRegion::DoContainsPoint(wxCoord x, wxCoord y) const +{ + if (!m_refData) + return wxOutRegion; + + CGPoint p = { y , x } ; + if (HIShapeContainsPoint( M_REGION , &p ) ) + return wxInRegion; + + return wxOutRegion; +} + +// Does the region contain the rectangle (x, y, w, h)? +wxRegionContain wxRegion::DoContainsRect(const wxRect& r) const +{ + if (!m_refData) + return wxOutRegion; + + CGRect rect = CGRectMake(r.x,r.y,r.width,r.height); + wxCFRef rectshape(HIShapeCreateWithRect(&rect)); + wxCFRef intersect(HIShapeCreateIntersection(rectshape,M_REGION)); + CGRect bounds; + HIShapeGetBounds(intersect, &bounds); + + if ( HIShapeIsRectangular(intersect) && CGRectEqualToRect(rect,bounds) ) + return wxInRegion; + else if ( HIShapeIsEmpty( intersect ) ) + return wxOutRegion; + else + return wxPartRegion; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// wxRegionIterator // +// // +/////////////////////////////////////////////////////////////////////////////// + +/*! + * Initialize empty iterator + */ +wxRegionIterator::wxRegionIterator() + : m_current(0), m_numRects(0), m_rects(NULL) +{ +} + +wxRegionIterator::~wxRegionIterator() +{ + if (m_rects) + { + delete [] m_rects; + m_rects = NULL; + } +} + +wxRegionIterator::wxRegionIterator(const wxRegionIterator& iterator) + : wxObject() + , m_current(iterator.m_current) + , m_numRects(0) + , m_rects(NULL) +{ + SetRects(iterator.m_numRects, iterator.m_rects); +} + +wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& iterator) +{ + m_current = iterator.m_current; + SetRects(iterator.m_numRects, iterator.m_rects); + + return *this; +} + +/*! + * Set iterator rects for region + */ +void wxRegionIterator::SetRects(long numRects, wxRect *rects) +{ + if (m_rects) + { + delete [] m_rects; + m_rects = NULL; + } + + if (rects && (numRects > 0)) + { + int i; + + m_rects = new wxRect[numRects]; + for (i = 0; i < numRects; i++) + m_rects[i] = rects[i]; + } + + m_numRects = numRects; +} + +/*! + * Initialize iterator for region + */ +wxRegionIterator::wxRegionIterator(const wxRegion& region) +{ + m_rects = NULL; + + Reset(region); +} + +/*! + * Reset iterator for a new /e region. + */ + +#ifndef __LP64__ +OSStatus wxMacRegionToRectsCounterCallback( + UInt16 message, RgnHandle WXUNUSED(region), const Rect *WXUNUSED(rect), void *data ) +{ + long *m_numRects = (long*) data ; + if ( message == kQDRegionToRectsMsgInit ) + { + (*m_numRects) = 0 ; + } + else if (message == kQDRegionToRectsMsgParse) + { + (*m_numRects) += 1 ; + } + + return noErr; +} + +class RegionToRectsCallbackData +{ +public : + wxRect* m_rects ; + long m_current ; +}; + +OSStatus wxMacRegionToRectsSetterCallback( + UInt16 message, RgnHandle WXUNUSED(region), const Rect *rect, void *data ) +{ + if (message == kQDRegionToRectsMsgParse) + { + RegionToRectsCallbackData *cb = (RegionToRectsCallbackData*) data ; + cb->m_rects[cb->m_current++] = wxRect( rect->left , rect->top , rect->right - rect->left , rect->bottom - rect->top ) ; + } + + return noErr; +} +#endif + +void wxRegionIterator::Reset(const wxRegion& region) +{ + m_current = 0; + m_region = region; + + if (m_rects) + { + delete [] m_rects; + m_rects = NULL; + } + + if (m_region.IsEmpty()) + { + m_numRects = 0; + } + else + { +#ifdef __LP64__ + // copying this to a path and dissecting the path would be an option + m_numRects = 1; + m_rects = new wxRect[m_numRects]; + m_rects[0] = m_region.GetBox(); + +#else + RegionToRectsUPP proc = (RegionToRectsUPP) wxMacRegionToRectsCounterCallback; + + OSStatus err = noErr; + RgnHandle rgn = NewRgn(); + HIShapeGetAsQDRgn(OTHER_M_REGION(region), rgn); + + err = QDRegionToRects (rgn, kQDParseRegionFromTopLeft, proc, (void*)&m_numRects); + if (err == noErr) + { + proc = (RegionToRectsUPP) wxMacRegionToRectsSetterCallback; + m_rects = new wxRect[m_numRects]; + RegionToRectsCallbackData data ; + data.m_rects = m_rects ; + data.m_current = 0 ; + QDRegionToRects( rgn , kQDParseRegionFromTopLeft, proc, (void*)&data ); + } + else + { + m_numRects = 0; + } + DisposeRgn( rgn ); +#endif + } +} + +/*! + * Increment iterator. The rectangle returned is the one after the + * incrementation. + */ +wxRegionIterator& wxRegionIterator::operator ++ () +{ + if (m_current < m_numRects) + ++m_current; + + return *this; +} + +/*! + * Increment iterator. The rectangle returned is the one before the + * incrementation. + */ +wxRegionIterator wxRegionIterator::operator ++ (int) +{ + wxRegionIterator previous(*this); + + if (m_current < m_numRects) + ++m_current; + + return previous; +} + +long wxRegionIterator::GetX() const +{ + if (m_current < m_numRects) + return m_rects[m_current].x; + + return 0; +} + +long wxRegionIterator::GetY() const +{ + if (m_current < m_numRects) + return m_rects[m_current].y; + + return 0; +} + +long wxRegionIterator::GetW() const +{ + if (m_current < m_numRects) + return m_rects[m_current].width ; + + return 0; +} + +long wxRegionIterator::GetH() const +{ + if (m_current < m_numRects) + return m_rects[m_current].height; + + return 0; +} diff --git a/src/osx/carbon/renderer.cpp b/src/osx/carbon/renderer.cpp new file mode 100644 index 0000000000..abcc1d6aa2 --- /dev/null +++ b/src/osx/carbon/renderer.cpp @@ -0,0 +1,408 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/renderer.cpp +// Purpose: implementation of wxRendererNative for Mac +// Author: Vadim Zeitlin +// Modified by: +// Created: 20.07.2003 +// RCS-ID: $Id$ +// Copyright: (c) 2003 Vadim Zeitlin +// License: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// for compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/string.h" + #include "wx/dc.h" + #include "wx/bitmap.h" + #include "wx/settings.h" + #include "wx/dcclient.h" + #include "wx/toplevel.h" +#endif + +#include "wx/renderer.h" +#include "wx/graphics.h" +#include "wx/mac/uma.h" + + +class WXDLLEXPORT wxRendererMac : public wxDelegateRendererNative +{ +public: + // draw the header control button (used by wxListCtrl) + virtual int DrawHeaderButton( wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0, + wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE, + wxHeaderButtonParams* params = NULL ); + + virtual int GetHeaderButtonHeight(wxWindow *win); + + // draw the expanded/collapsed icon for a tree control item + virtual void DrawTreeItemButton( wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0 ); + + // draw a (vertical) sash + virtual void DrawSplitterSash( wxWindow *win, + wxDC& dc, + const wxSize& size, + wxCoord position, + wxOrientation orient, + int flags = 0 ); + + virtual void DrawCheckBox(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0); + + virtual void DrawComboBoxDropButton(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0); + + virtual void DrawPushButton(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0); + + virtual void DrawItemSelectionRect(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0); + + virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0); + +private: + void DrawMacThemeButton(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags, + int kind, + int adornment); + + // the tree buttons + wxBitmap m_bmpTreeExpanded; + wxBitmap m_bmpTreeCollapsed; +}; + +// ============================================================================ +// implementation +// ============================================================================ + +// static +wxRendererNative& wxRendererNative::GetDefault() +{ + static wxRendererMac s_rendererMac; + + return s_rendererMac; +} + +int wxRendererMac::DrawHeaderButton( wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags, + wxHeaderSortIconType sortArrow, + wxHeaderButtonParams* params ) +{ + const wxCoord x = rect.x; + const wxCoord y = rect.y; + const wxCoord w = rect.width; + const wxCoord h = rect.height; + + dc.SetBrush( *wxTRANSPARENT_BRUSH ); + + HIRect headerRect = CGRectMake( x, y, w, h ); + if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) ) + { + win->Refresh( &rect ); + } + else + { + CGContextRef cgContext; + wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl(); + + cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext(); + + { + HIThemeButtonDrawInfo drawInfo; + HIRect labelRect; + + memset( &drawInfo, 0, sizeof(drawInfo) ); + drawInfo.version = 0; + drawInfo.kind = kThemeListHeaderButton; + drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive; + drawInfo.value = (flags & wxCONTROL_SELECTED) ? kThemeButtonOn : kThemeButtonOff; + drawInfo.adornment = kThemeAdornmentNone; + + // The down arrow is drawn automatically, change it to an up arrow if needed. + if ( sortArrow == wxHDR_SORT_ICON_UP ) + drawInfo.adornment = kThemeAdornmentHeaderButtonSortUp; + + HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect ); + + // If we don't want any arrows we need to draw over the one already there + if ( (flags & wxCONTROL_SELECTED) && (sortArrow == wxHDR_SORT_ICON_NONE) ) + { + // clip to the header rectangle + CGContextSaveGState( cgContext ); + CGContextClipToRect( cgContext, headerRect ); + // but draw bigger than that so the arrow will get clipped off + headerRect.size.width += 25; + HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect ); + CGContextRestoreGState( cgContext ); + } + } + } + + // Reserve room for the arrows before writing the label, and turn off the + // flags we've already handled + wxRect newRect(rect); + if ( (flags & wxCONTROL_SELECTED) && (sortArrow != wxHDR_SORT_ICON_NONE) ) + { + newRect.width -= 12; + sortArrow = wxHDR_SORT_ICON_NONE; + } + flags &= ~wxCONTROL_SELECTED; + + return DrawHeaderButtonContents(win, dc, newRect, flags, sortArrow, params); +} + + +int wxRendererMac::GetHeaderButtonHeight(wxWindow* WXUNUSED(win)) +{ + SInt32 standardHeight; + OSStatus errStatus; + + errStatus = GetThemeMetric( kThemeMetricListHeaderHeight, &standardHeight ); + if (errStatus == noErr) + { + return standardHeight; + } + return -1; +} + +void wxRendererMac::DrawTreeItemButton( wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags ) +{ + // now the wxGCDC is using native transformations + const wxCoord x = rect.x; + const wxCoord y = rect.y; + const wxCoord w = rect.width; + const wxCoord h = rect.height; + + dc.SetBrush( *wxTRANSPARENT_BRUSH ); + + HIRect headerRect = CGRectMake( x, y, w, h ); + if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) ) + { + win->Refresh( &rect ); + } + else + { + CGContextRef cgContext; + + wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl(); + cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext(); + + HIThemeButtonDrawInfo drawInfo; + HIRect labelRect; + + memset( &drawInfo, 0, sizeof(drawInfo) ); + drawInfo.version = 0; + drawInfo.kind = kThemeDisclosureButton; + drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive; + // Apple mailing list posts say to use the arrow adornment constants, but those don't work. + // We need to set the value using the 'old' DrawThemeButton constants instead. + drawInfo.value = (flags & wxCONTROL_EXPANDED) ? kThemeDisclosureDown : kThemeDisclosureRight; + drawInfo.adornment = kThemeAdornmentNone; + + HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect ); + } +} + +void wxRendererMac::DrawSplitterSash( wxWindow *win, + wxDC& dc, + const wxSize& size, + wxCoord position, + wxOrientation orient, + int WXUNUSED(flags) ) +{ + bool hasMetal = win->MacGetTopLevelWindow()->MacGetMetalAppearance(); + SInt32 height; + GetThemeMetric( kThemeMetricSmallPaneSplitterHeight, &height ); + HIRect splitterRect; + if (orient == wxVERTICAL) + splitterRect = CGRectMake( position, 0, height, size.y ); + else + splitterRect = CGRectMake( 0, position, size.x, height ); + + // under compositing we should only draw when called by the OS, otherwise just issue a redraw command + // strange redraw errors occur if we don't do this + + if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) ) + { + wxRect rect( (int) splitterRect.origin.x, (int) splitterRect.origin.y, (int) splitterRect.size.width, + (int) splitterRect.size.height ); + win->Refresh( &rect ); + } + else + { + CGContextRef cgContext; + wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl(); + cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext(); + + HIThemeSplitterDrawInfo drawInfo; + drawInfo.version = 0; + drawInfo.state = kThemeStateActive; + drawInfo.adornment = hasMetal ? kHIThemeSplitterAdornmentMetal : kHIThemeSplitterAdornmentNone; + HIThemeDrawPaneSplitter( &splitterRect, &drawInfo, cgContext, kHIThemeOrientationNormal ); + } +} + +void +wxRendererMac::DrawItemSelectionRect(wxWindow * WXUNUSED(win), + wxDC& dc, + const wxRect& rect, + int flags) +{ + if ( !(flags & wxCONTROL_SELECTED) ) + return; + + wxColour col( wxMacCreateCGColorFromHITheme( (flags & wxCONTROL_FOCUSED) ? + kThemeBrushAlternatePrimaryHighlightColor + : kThemeBrushSecondaryHighlightColor ) ); + wxBrush selBrush( col ); + + dc.SetPen( *wxTRANSPARENT_PEN ); + dc.SetBrush( selBrush ); + dc.DrawRectangle( rect ); +} + + +void +wxRendererMac::DrawMacThemeButton(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags, + int kind, + int adornment) +{ + // now the wxGCDC is using native transformations + const wxCoord x = rect.x; + const wxCoord y = rect.y; + const wxCoord w = rect.width; + const wxCoord h = rect.height; + + dc.SetBrush( *wxTRANSPARENT_BRUSH ); + + HIRect headerRect = CGRectMake( x, y, w, h ); + if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) ) + { + win->Refresh( &rect ); + } + else + { + wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl(); + CGContextRef cgContext; + cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext(); + + HIThemeButtonDrawInfo drawInfo; + HIRect labelRect; + + memset( &drawInfo, 0, sizeof(drawInfo) ); + drawInfo.version = 0; + drawInfo.kind = kind; + drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive; + drawInfo.value = (flags & wxCONTROL_SELECTED) ? kThemeButtonOn : kThemeButtonOff; + if (flags & wxCONTROL_UNDETERMINED) + drawInfo.value = kThemeButtonMixed; + drawInfo.adornment = adornment; + + HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect ); + } +} + +void +wxRendererMac::DrawCheckBox(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags) +{ + if (flags & wxCONTROL_CHECKED) + flags |= wxCONTROL_SELECTED; + + DrawMacThemeButton(win, dc, rect, flags, + kThemeCheckBox, kThemeAdornmentNone); +} + +void +wxRendererMac::DrawComboBoxDropButton(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags) +{ + int kind; + if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL)) + kind = kThemeArrowButtonSmall; + else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI)) + kind = kThemeArrowButtonMini; + else + kind = kThemeArrowButton; + + DrawMacThemeButton(win, dc, rect, flags, + kind, kThemeAdornmentArrowDownArrow); +} + +void +wxRendererMac::DrawPushButton(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags) +{ + int kind; + if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL)) + kind = kThemeBevelButtonSmall; + // There is no kThemeBevelButtonMini, but in this case, use Small + else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI)) + kind = kThemeBevelButtonSmall; + else + kind = kThemeBevelButton; + + DrawMacThemeButton(win, dc, rect, flags, + kind, kThemeAdornmentNone); +} + +void +wxRendererMac::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags) +{ + if (!win) + { + wxDelegateRendererNative::DrawFocusRect(win, dc, rect, flags); + return; + } + + CGRect cgrect = CGRectMake( rect.x , rect.y , rect.width, rect.height ) ; + + HIThemeFrameDrawInfo info ; + memset( &info, 0 , sizeof(info) ) ; + + info.version = 0 ; + info.kind = 0 ; + info.state = kThemeStateActive; + info.isFocused = true ; + + CGContextRef cgContext = (CGContextRef) win->MacGetCGContextRef() ; + wxASSERT( cgContext ) ; + + HIThemeDrawFocusRect( &cgrect , true , cgContext , kHIThemeOrientationNormal ) ; +} + diff --git a/src/osx/carbon/scrolbar.cpp b/src/osx/carbon/scrolbar.cpp new file mode 100644 index 0000000000..c7c1263451 --- /dev/null +++ b/src/osx/carbon/scrolbar.cpp @@ -0,0 +1,247 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/scrolbar.cpp +// Purpose: wxScrollBar +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/scrolbar.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/settings.h" +#endif + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxScrollBar, wxControl) + +BEGIN_EVENT_TABLE(wxScrollBar, wxControl) +END_EVENT_TABLE() + + +bool wxScrollBar::Create( wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_macIsUserPane = false; + + if ( !wxControl::Create( parent, id, pos, size, style, validator, name ) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + + m_peer = new wxMacControl( this ); + OSStatus err = CreateScrollBarControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, + 0, 0, 100, 1, true /* liveTracking */, + GetwxMacLiveScrollbarActionProc(), + m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + MacPostControlCreate( pos, size ); + + return true; +} + +wxScrollBar::~wxScrollBar() +{ +} + +void wxScrollBar::SetThumbPosition( int viewStart ) +{ + m_peer->SetValue( viewStart ); +} + +int wxScrollBar::GetThumbPosition() const +{ + return m_peer->GetValue(); +} + +void wxScrollBar::SetScrollbar( int position, + int thumbSize, + int range, + int pageSize, + bool WXUNUSED(refresh) ) +{ + m_pageSize = pageSize; + m_viewSize = thumbSize; + m_objectSize = range; + + int range1 = wxMax( (m_objectSize - m_viewSize), 0 ); + + m_peer->SetMinimum( 0 ); + m_peer->SetMaximum( range1 ); + m_peer->SetValue( position ); + m_peer->SetViewSize( m_viewSize ); +} + +void wxScrollBar::Command( wxCommandEvent& event ) +{ + SetThumbPosition( event.GetInt() ); + ProcessCommand( event ); +} + +void wxScrollBar::MacHandleControlClick( WXWidget WXUNUSED(control), wxInt16 controlpart, bool mouseStillDown ) +{ + int position = m_peer->GetValue(); + int minPos = m_peer->GetMinimum(); + int maxPos = m_peer->GetMaximum(); + + wxEventType scrollEvent = wxEVT_NULL; + int nScrollInc = 0; + + // all events have already been reported during mouse down, except for THUMBRELEASE + if ( !mouseStillDown && controlpart != kControlIndicatorPart ) + return; + + switch ( controlpart ) + { + case kControlUpButtonPart: + nScrollInc = -1; + scrollEvent = wxEVT_SCROLL_LINEUP; + break; + + case kControlDownButtonPart: + nScrollInc = 1; + scrollEvent = wxEVT_SCROLL_LINEDOWN; + break; + + case kControlPageUpPart: + nScrollInc = -m_pageSize; + scrollEvent = wxEVT_SCROLL_PAGEUP; + break; + + case kControlPageDownPart: + nScrollInc = m_pageSize; + scrollEvent = wxEVT_SCROLL_PAGEDOWN; + break; + + case kControlIndicatorPart: + nScrollInc = 0; + if ( mouseStillDown ) + scrollEvent = wxEVT_SCROLL_THUMBTRACK; + else + scrollEvent = wxEVT_SCROLL_THUMBRELEASE; + break; + + default: + wxFAIL_MSG(wxT("unknown scrollbar selector")); + break; + } + + int new_pos = position + nScrollInc; + + if (new_pos < minPos) + new_pos = minPos; + else if (new_pos > maxPos) + new_pos = maxPos; + + if ( nScrollInc ) + SetThumbPosition( new_pos ); + + wxScrollEvent event( scrollEvent, m_windowId ); + if ( m_windowStyle & wxHORIZONTAL ) + event.SetOrientation( wxHORIZONTAL ); + else + event.SetOrientation( wxVERTICAL ); + + event.SetPosition( new_pos ); + event.SetEventObject( this ); + + wxWindow* window = GetParent(); + if (window && window->MacIsWindowScrollbar( this )) + // this is hardcoded + window->MacOnScroll( event ); + else + HandleWindowEvent( event ); +} + +wxInt32 wxScrollBar::MacControlHit( WXEVENTHANDLERREF WXUNUSED(handler), WXEVENTREF mevent ) +{ + int position = m_peer->GetValue(); + int minPos = m_peer->GetMinimum(); + int maxPos = m_peer->GetMaximum(); + + wxEventType scrollEvent = wxEVT_NULL; + int nScrollInc = 0; + + wxMacCarbonEvent cEvent( (EventRef)mevent ); + ControlPartCode controlpart = cEvent.GetParameter(kEventParamControlPart, typeControlPartCode); + + // all events have already been reported during mouse down, except for THUMBRELEASE + // NB: this may need to be reviewed in light of the fact that scroll wheel events + // aren't being handled properly + if ( controlpart != kControlIndicatorPart ) + return eventNotHandledErr; + + switch ( controlpart ) + { + case kControlIndicatorPart: + nScrollInc = 0; + scrollEvent = wxEVT_SCROLL_THUMBRELEASE; + break; + + default: + wxFAIL_MSG(wxT("unknown scrollbar selector")); + break; + } + + int new_pos = position + nScrollInc; + + if (new_pos < minPos) + new_pos = minPos; + else if (new_pos > maxPos) + new_pos = maxPos; + + if ( nScrollInc ) + SetThumbPosition( new_pos ); + + wxScrollEvent event( scrollEvent, m_windowId ); + if ( m_windowStyle & wxHORIZONTAL ) + event.SetOrientation( wxHORIZONTAL ); + else + event.SetOrientation( wxVERTICAL ); + + event.SetPosition( new_pos ); + event.SetEventObject( this ); + wxWindow* window = GetParent(); + if (window && window->MacIsWindowScrollbar( this )) + // this is hardcoded + window->MacOnScroll( event ); + else + HandleWindowEvent( event ); + + return noErr; +} + + +wxSize wxScrollBar::DoGetBestSize() const +{ + int w = 100; + int h = 100; + + if ( IsVertical() ) + { + w = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + } + else + { + h = wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y); + } + + wxSize best(w, h); + CacheBestSize(best); + return best; +} diff --git a/src/osx/carbon/settings.cpp b/src/osx/carbon/settings.cpp new file mode 100644 index 0000000000..58cecfb357 --- /dev/null +++ b/src/osx/carbon/settings.cpp @@ -0,0 +1,256 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/settings.cpp +// Purpose: wxSettings +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/settings.h" + +#ifndef WX_PRECOMP + #include "wx/utils.h" + #include "wx/gdicmn.h" +#endif + +#include "wx/mac/uma.h" + +// ---------------------------------------------------------------------------- +// wxSystemSettingsNative +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// colours +// ---------------------------------------------------------------------------- + +wxColour wxSystemSettingsNative::GetColour(wxSystemColour index) +{ + wxColour resultColor; + ThemeBrush colorBrushID; + + switch ( index ) + { + case wxSYS_COLOUR_WINDOW: + resultColor = *wxWHITE ; + break ; + case wxSYS_COLOUR_SCROLLBAR : + case wxSYS_COLOUR_BACKGROUND: + case wxSYS_COLOUR_ACTIVECAPTION: + case wxSYS_COLOUR_INACTIVECAPTION: + case wxSYS_COLOUR_MENU: + case wxSYS_COLOUR_WINDOWFRAME: + case wxSYS_COLOUR_ACTIVEBORDER: + case wxSYS_COLOUR_INACTIVEBORDER: + case wxSYS_COLOUR_BTNFACE: + case wxSYS_COLOUR_MENUBAR: + resultColor = wxColor( 0xDD, 0xDD, 0xDD ); + break ; + + case wxSYS_COLOUR_LISTBOX : + resultColor = *wxWHITE ; + break ; + + case wxSYS_COLOUR_BTNSHADOW: + resultColor = wxColor( 0xBE, 0xBE, 0xBE ); + break ; + + case wxSYS_COLOUR_BTNTEXT: + case wxSYS_COLOUR_MENUTEXT: + case wxSYS_COLOUR_WINDOWTEXT: + case wxSYS_COLOUR_CAPTIONTEXT: + case wxSYS_COLOUR_INFOTEXT: + case wxSYS_COLOUR_INACTIVECAPTIONTEXT: + resultColor = *wxBLACK; + break ; + + case wxSYS_COLOUR_HIGHLIGHT: + { +#if 0 + // NB: enable this case as desired + colorBrushID = kThemeBrushAlternatePrimaryHighlightColor; +#else + colorBrushID = kThemeBrushPrimaryHighlightColor; +#endif + resultColor = wxColor( wxMacCreateCGColorFromHITheme(colorBrushID) ); + } + break ; + + case wxSYS_COLOUR_BTNHIGHLIGHT: + case wxSYS_COLOUR_GRAYTEXT: + resultColor = wxColor( 0xCC, 0xCC, 0xCC ); + break ; + + case wxSYS_COLOUR_3DDKSHADOW: + resultColor = wxColor( 0x44, 0x44, 0x44 ); + break ; + + case wxSYS_COLOUR_3DLIGHT: + resultColor = wxColor( 0xCC, 0xCC, 0xCC ); + break ; + + case wxSYS_COLOUR_HIGHLIGHTTEXT : +#if 0 + // NB: enable this case as desired + resultColor = *wxWHITE ; +#else + { + wxColour highlightcolor( wxMacCreateCGColorFromHITheme(kThemeBrushPrimaryHighlightColor) ); + if ((highlightcolor.Red() + highlightcolor.Green() + highlightcolor.Blue() ) == 0) + resultColor = *wxWHITE ; + else + resultColor = *wxBLACK ; + } +#endif + break ; + + case wxSYS_COLOUR_INFOBK : + // we don't have a way to detect tooltip color, so use the + // standard value used at least on 10.4: + resultColor = wxColour( 0xFF, 0xFF, 0xD3 ) ; + break ; + case wxSYS_COLOUR_APPWORKSPACE: + resultColor = wxColor( 0x80, 0x80, 0x80 ); ; + break ; + + case wxSYS_COLOUR_HOTLIGHT: + case wxSYS_COLOUR_GRADIENTACTIVECAPTION: + case wxSYS_COLOUR_GRADIENTINACTIVECAPTION: + case wxSYS_COLOUR_MENUHILIGHT: + // TODO: + resultColor = *wxBLACK; + break ; + + // case wxSYS_COLOUR_MAX: + default: + resultColor = *wxWHITE; + // wxCHECK_MSG( index >= wxSYS_COLOUR_MAX, false, _T("unknown system colour index") ); + break ; + } + + return resultColor; +} + +// ---------------------------------------------------------------------------- +// fonts +// ---------------------------------------------------------------------------- + +wxFont wxSystemSettingsNative::GetFont(wxSystemFont index) +{ + switch (index) + { + case wxSYS_ANSI_VAR_FONT : + case wxSYS_SYSTEM_FONT : + case wxSYS_DEVICE_DEFAULT_FONT : + case wxSYS_DEFAULT_GUI_FONT : + return *wxSMALL_FONT ; + break ; + + default : + break ; + } + + return *wxNORMAL_FONT; +} + +// ---------------------------------------------------------------------------- +// system metrics/features +// ---------------------------------------------------------------------------- + +// Get a system metric, e.g. scrollbar size +int wxSystemSettingsNative::GetMetric(wxSystemMetric index, wxWindow* WXUNUSED(win)) +{ + int value; + + switch ( index ) + { + case wxSYS_MOUSE_BUTTONS: + // we emulate a two button mouse (ctrl + click = right button) + return 2; + + // TODO case wxSYS_BORDER_X: + // TODO case wxSYS_BORDER_Y: + // TODO case wxSYS_CURSOR_X: + // TODO case wxSYS_CURSOR_Y: + // TODO case wxSYS_DCLICK_X: + // TODO case wxSYS_DCLICK_Y: + // TODO case wxSYS_DRAG_X: + // TODO case wxSYS_DRAG_Y: + // TODO case wxSYS_EDGE_X: + // TODO case wxSYS_EDGE_Y: + + case wxSYS_HSCROLL_ARROW_X: + case wxSYS_HSCROLL_ARROW_Y: + case wxSYS_HTHUMB_X: + return 16; + + // TODO case wxSYS_ICON_X: + // TODO case wxSYS_ICON_Y: + // TODO case wxSYS_ICONSPACING_X: + // TODO case wxSYS_ICONSPACING_Y: + // TODO case wxSYS_WINDOWMIN_X: + // TODO case wxSYS_WINDOWMIN_Y: + + case wxSYS_SCREEN_X: + wxDisplaySize( &value, NULL ); + return value; + + case wxSYS_SCREEN_Y: + wxDisplaySize( NULL, &value ); + return value; + + // TODO case wxSYS_FRAMESIZE_X: + // TODO case wxSYS_FRAMESIZE_Y: + // TODO case wxSYS_SMALLICON_X: + // TODO case wxSYS_SMALLICON_Y: + + case wxSYS_HSCROLL_Y: + case wxSYS_VSCROLL_X: + case wxSYS_VSCROLL_ARROW_X: + case wxSYS_VSCROLL_ARROW_Y: + case wxSYS_VTHUMB_Y: + return 16; + + case wxSYS_PENWINDOWS_PRESENT: + return 0; + + case wxSYS_SWAP_BUTTONS: + return 0; + + // TODO: case wxSYS_CAPTION_Y: + // TODO: case wxSYS_MENU_Y: + // TODO: case wxSYS_NETWORK_PRESENT: + // TODO: case wxSYS_SHOW_SOUNDS: + + case wxSYS_DCLICK_MSEC: +#ifdef __LP64__ + // default on mac is 30 ticks, we shouldn't really use wxSYS_DCLICK_MSEC anyway + // but rather rely on the 'click-count' by the system delivered in a mouse event + return 500; +#else + return (int)(GetDblTime() * 1000. / 60.); +#endif + default: + // unsupported metric + break; + } + + return -1; +} + +bool wxSystemSettingsNative::HasFeature(wxSystemFeature index) +{ + switch (index) + { + case wxSYS_CAN_ICONIZE_FRAME: + case wxSYS_CAN_DRAW_FRAME_DECORATIONS: + return true; + + default: + return false; + } +} diff --git a/src/osx/carbon/slider.cpp b/src/osx/carbon/slider.cpp new file mode 100644 index 0000000000..ced51f397e --- /dev/null +++ b/src/osx/carbon/slider.cpp @@ -0,0 +1,557 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: slider.cpp +// Purpose: wxSlider +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_SLIDER + +#include "wx/slider.h" +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl) + +BEGIN_EVENT_TABLE(wxSlider, wxControl) +END_EVENT_TABLE() + + // The dimensions of the different styles of sliders (from Aqua document) +#define wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS 24 +#define wxSLIDER_DIMENSIONACROSS_ARROW 18 + +// Distance between slider and text +#define wxSLIDER_BORDERTEXT 5 + +// NB: The default orientation for a slider is horizontal; however, if the user specifies +// some slider styles but doesn't specify the orientation we have to assume he wants a +// horizontal one. Therefore in this file when testing for the slider's orientation +// vertical is tested for if this is not set then we use the horizontal one +// e.g., if (GetWindowStyle() & wxSL_VERTICAL) {} else { horizontal case }. + +wxSlider::wxSlider() +{ + m_pageSize = 1; + m_lineSize = 1; + m_rangeMax = 0; + m_rangeMin = 0; + m_tickFreq = 0; + + m_macMinimumStatic = NULL; + m_macMaximumStatic = NULL; + m_macValueStatic = NULL; +} + +bool wxSlider::Create(wxWindow *parent, + wxWindowID id, + int value, int minValue, int maxValue, + const wxPoint& pos, + const wxSize& size, long style, + const wxValidator& validator, + const wxString& name) +{ + m_macIsUserPane = false; + + m_macMinimumStatic = NULL; + m_macMaximumStatic = NULL; + m_macValueStatic = NULL; + + m_lineSize = 1; + m_tickFreq = 0; + + m_rangeMax = maxValue; + m_rangeMin = minValue; + + m_pageSize = (int)((maxValue - minValue) / 10); + + // our styles are redundant: wxSL_LEFT/RIGHT imply wxSL_VERTICAL and + // wxSL_TOP/BOTTOM imply wxSL_HORIZONTAL, but for backwards compatibility + // reasons we can't really change it, instead try to infer the orientation + // from the flags given to us here + switch ( style & (wxSL_LEFT | wxSL_RIGHT | wxSL_TOP | wxSL_BOTTOM) ) + { + case wxSL_LEFT: + case wxSL_RIGHT: + style |= wxSL_VERTICAL; + break; + + case wxSL_TOP: + case wxSL_BOTTOM: + style |= wxSL_HORIZONTAL; + break; + + case 0: + default: + // no specific direction, do we have at least the orientation? + if ( !(style & (wxSL_HORIZONTAL | wxSL_VERTICAL)) ) + // no: choose default + style |= wxSL_BOTTOM | wxSL_HORIZONTAL; + break; + } + + wxASSERT_MSG( !(style & wxSL_VERTICAL) || !(style & wxSL_HORIZONTAL), + wxT("incompatible slider direction and orientation") ); + + if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ); + + // NB: (RN) Ticks here are sometimes off in the GUI if there + // are not as many tick marks as there are values + // + int tickMarks = 0; + if ( style & wxSL_AUTOTICKS ) + tickMarks = (maxValue - minValue) + 1; // +1 for the 0 value + + // keep the number of tickmarks from becoming unwieldly, therefore below it is ok to cast + // it to a UInt16 + while (tickMarks > 20) + tickMarks /= 5; + + m_peer = new wxMacControl( this ); + OSStatus err = CreateSliderControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, + value, minValue, maxValue, + kControlSliderPointsDownOrRight, + (UInt16) tickMarks, true /* liveTracking */, + GetwxMacLiveScrollbarActionProc(), + m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + if (style & wxSL_VERTICAL) + // Forces SetSize to use the proper width + SetSizeHints(10, -1, 10, -1); + else + // Forces SetSize to use the proper height + SetSizeHints(-1, 10, -1, 10); + + // NB: SetSizeHints is overloaded by wxSlider and will substitute 10 with the + // proper dimensions, it also means other people cannot bugger the slider with + // other values + + if (style & wxSL_LABELS) + { + m_macMinimumStatic = new wxStaticText( parent, wxID_ANY, wxEmptyString ); + m_macMaximumStatic = new wxStaticText( parent, wxID_ANY, wxEmptyString ); + m_macValueStatic = new wxStaticText( parent, wxID_ANY, wxEmptyString ); + } + + SetRange(minValue, maxValue); + SetValue(value); + + MacPostControlCreate(pos, size); + + return true; +} + +wxSlider::~wxSlider() +{ + // this is a special case, as we had to add windows as siblings we are + // responsible for their disposal, but only if we are not part of a DestroyAllChildren + if ( m_parent && !m_parent->IsBeingDeleted() ) + { + delete m_macMinimumStatic; + delete m_macMaximumStatic; + delete m_macValueStatic; + } +} + +int wxSlider::GetValue() const +{ + // We may need to invert the value returned by the widget + return ValueInvertOrNot( m_peer->GetValue() ) ; +} + +void wxSlider::SetValue(int value) +{ + if ( m_macValueStatic ) + { + wxString valuestring; + valuestring.Printf( wxT("%d"), value ); + m_macValueStatic->SetLabel( valuestring ); + } + + // We only invert for the setting of the actual native widget + m_peer->SetValue( ValueInvertOrNot( value ) ); +} + +void wxSlider::SetRange(int minValue, int maxValue) +{ + wxString value; + + m_rangeMin = minValue; + m_rangeMax = maxValue; + + m_peer->SetMinimum( m_rangeMin ); + m_peer->SetMaximum( m_rangeMax ); + + if (m_macMinimumStatic) + { + value.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMin ) ); + m_macMinimumStatic->SetLabel( value ); + } + + if (m_macMaximumStatic) + { + value.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMax ) ); + m_macMaximumStatic->SetLabel( value ); + } + + // If the range is out of bounds, set it to a + // value that is within bounds + // RN: Testing reveals OSX does its own + // bounding, perhaps this isn't needed? + int currentValue = GetValue(); + + if(currentValue < m_rangeMin) + SetValue(m_rangeMin); + else if(currentValue > m_rangeMax) + SetValue(m_rangeMax); +} + +// For trackbars only +void wxSlider::SetTickFreq(int n, int WXUNUSED(pos)) +{ + // TODO + m_tickFreq = n; +} + +void wxSlider::SetPageSize(int pageSize) +{ + // TODO + m_pageSize = pageSize; +} + +int wxSlider::GetPageSize() const +{ + return m_pageSize; +} + +void wxSlider::ClearSel() +{ + // TODO +} + +void wxSlider::ClearTicks() +{ + // TODO +} + +void wxSlider::SetLineSize(int lineSize) +{ + m_lineSize = lineSize; + // TODO +} + +int wxSlider::GetLineSize() const +{ + // TODO + return m_lineSize; +} + +int wxSlider::GetSelEnd() const +{ + // TODO + return 0; +} + +int wxSlider::GetSelStart() const +{ + // TODO + return 0; +} + +void wxSlider::SetSelection(int WXUNUSED(minPos), int WXUNUSED(maxPos)) +{ + // TODO +} + +void wxSlider::SetThumbLength(int WXUNUSED(len)) +{ + // TODO +} + +int wxSlider::GetThumbLength() const +{ + // TODO + return 0; +} + +void wxSlider::SetTick(int WXUNUSED(tickPos)) +{ + // TODO +} + +void wxSlider::Command(wxCommandEvent &event) +{ + SetValue(event.GetInt()); + ProcessCommand(event); +} + +void wxSlider::MacHandleControlClick(WXWidget WXUNUSED(control), + wxInt16 WXUNUSED(controlpart), + bool WXUNUSED(mouseStillDown)) +{ + // Whatever the native value is, we may need to invert it for calling + // SetValue and putting the possibly inverted value in the event + int value = ValueInvertOrNot( m_peer->GetValue() ); + + SetValue( value ); + + wxScrollEvent event( wxEVT_SCROLL_THUMBTRACK, m_windowId ); + event.SetPosition( value ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + + wxCommandEvent cevent( wxEVT_COMMAND_SLIDER_UPDATED, m_windowId ); + cevent.SetInt( value ); + cevent.SetEventObject( this ); + HandleWindowEvent( cevent ); +} + +wxInt32 wxSlider::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler), + WXEVENTREF WXUNUSED(mevent)) +{ + // Whatever the native value is, we may need to invert it for calling + // SetValue and putting the possibly inverted value in the event + int value = ValueInvertOrNot( m_peer->GetValue() ) ; + + SetValue( value ) ; + + wxScrollEvent event( wxEVT_SCROLL_THUMBRELEASE, m_windowId ); + event.SetPosition( value ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + + wxCommandEvent cevent( wxEVT_COMMAND_SLIDER_UPDATED, m_windowId ); + cevent.SetInt( value ); + cevent.SetEventObject( this ); + + HandleWindowEvent( cevent ); + + return noErr; +} + +// This is overloaded in wxSlider so that the proper width/height will always be used +// for the slider different values would cause redrawing and mouse detection problems +// +void wxSlider::DoSetSizeHints( int minW, int minH, + int maxW, int maxH, + int WXUNUSED(incW), int WXUNUSED(incH) ) +{ + wxSize size = GetBestSize(); + + if (GetWindowStyle() & wxSL_VERTICAL) + { + SetMinSize( wxSize(size.x,minH) ); + SetMaxSize( wxSize(size.x,maxH) ); + } + else + { + SetMinSize( wxSize(minW,size.y) ); + SetMaxSize( wxSize(maxW,size.y) ); + } +} + +wxSize wxSlider::DoGetBestSize() const +{ + wxSize size; + int textwidth, textheight; + int mintwidth, mintheight; + int maxtwidth, maxtheight; + + textwidth = textheight = 0; + mintwidth = mintheight = 0; + maxtwidth = maxtheight = 0; + + if (GetWindowStyle() & wxSL_LABELS) + { + wxString text; + + // Get maximum text label width and height + text.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMin ) ); + GetTextExtent(text, &mintwidth, &mintheight); + text.Printf( wxT("%d"), ValueInvertOrNot( m_rangeMax ) ); + GetTextExtent(text, &maxtwidth, &maxtheight); + + if (maxtheight > mintheight) + textheight = maxtheight; + else + textheight = mintheight; + + if (maxtwidth > mintwidth) + textwidth = maxtwidth; + else + textwidth = mintwidth; + } + + if (GetWindowStyle() & wxSL_VERTICAL) + { + size.y = 150; + + if (GetWindowStyle() & wxSL_AUTOTICKS) + size.x = wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS; + else + size.x = wxSLIDER_DIMENSIONACROSS_ARROW; + + if (GetWindowStyle() & wxSL_LABELS) + size.x += textwidth + wxSLIDER_BORDERTEXT; + } + else + { + size.x = 150; + + if (GetWindowStyle() & wxSL_AUTOTICKS) + size.y = wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS; + else + size.y = wxSLIDER_DIMENSIONACROSS_ARROW; + + if (GetWindowStyle() & wxSL_LABELS) + { + size.y += textheight + wxSLIDER_BORDERTEXT; + size.x += (mintwidth / 2) + (maxtwidth / 2); + } + } + + return size; +} + +void wxSlider::DoSetSize(int x, int y, int w, int h, int sizeFlags) +{ + int yborder = 0; + int minValWidth, maxValWidth, textheight; + int sliderBreadth; + int width = w; + + if (GetWindowStyle() & wxSL_LABELS) + { + wxString text; + int ht, valValWidth; + + // Get maximum text label width and height + text.Printf(wxT("%d"), ValueInvertOrNot( m_rangeMin ) ); + GetTextExtent(text, &minValWidth, &textheight); + text.Printf(wxT("%d"), ValueInvertOrNot( m_rangeMax ) ); + GetTextExtent(text, &maxValWidth, &ht); + + if (ht > textheight) + textheight = ht; + + if (GetWindowStyle() & wxSL_HORIZONTAL) + { + if ( m_macMinimumStatic ) + { + w -= minValWidth / 2; + x += minValWidth / 2; + } + + if ( m_macMaximumStatic ) + w -= maxValWidth / 2; + } + + // Labels have this control's parent as their parent + // so if this control is not at 0,0 relative to the parent + // the labels need to know the position of this control + // relative to its parent in order to size properly, so + // move the control first so we can use GetPosition() + wxControl::DoSetSize( x, y, w, h, sizeFlags ); + + if (GetWindowStyle() & wxSL_VERTICAL) + // If vertical, use current value + text.Printf(wxT("%d"), (int)m_peer->GetValue()); + else + // Use max so that the current value doesn't drift as centering would need to change + text.Printf(wxT("%d"), m_rangeMax); + + GetTextExtent(text, &valValWidth, &ht); + + yborder = textheight + wxSLIDER_BORDERTEXT; + + // Get slider breadth + if (GetWindowStyle() & wxSL_AUTOTICKS) + sliderBreadth = wxSLIDER_DIMENSIONACROSS_WITHTICKMARKS; + else + sliderBreadth = wxSLIDER_DIMENSIONACROSS_ARROW; + + if (GetWindowStyle() & wxSL_VERTICAL) + { + h = h - yborder; + + if ( m_macMinimumStatic ) + m_macMinimumStatic->Move(GetPosition().x + sliderBreadth + wxSLIDER_BORDERTEXT, GetPosition().y + h - yborder); + if ( m_macMaximumStatic ) + m_macMaximumStatic->Move(GetPosition().x + sliderBreadth + wxSLIDER_BORDERTEXT, GetPosition().y + 0); + if ( m_macValueStatic ) + m_macValueStatic->Move(GetPosition().x + sliderBreadth + wxSLIDER_BORDERTEXT, GetPosition().y + (h / 2) - (ht / 2)); + } + else + { + if ( m_macMinimumStatic ) + m_macMinimumStatic->Move(GetPosition().x, GetPosition().y + sliderBreadth + wxSLIDER_BORDERTEXT); + if ( m_macMaximumStatic ) + m_macMaximumStatic->Move(GetPosition().x + w - maxValWidth, GetPosition().y + sliderBreadth + wxSLIDER_BORDERTEXT); + if ( m_macValueStatic ) + m_macValueStatic->Move(GetPosition().x + (w / 2) - (valValWidth / 2), GetPosition().y + sliderBreadth + wxSLIDER_BORDERTEXT); + } + } + + // yet another hack since this is a composite control + // when wxSlider has it's size hardcoded, we're not allowed to + // change the size. But when the control has labels, we DO need + // to resize the internal Mac control to accommodate the text labels. + // We need to trick the wxWidgets resize mechanism so that we can + // resize the slider part of the control ONLY. + + // TODO: Can all of this code go in the conditional wxSL_LABELS block? + + int minWidth = m_minWidth; + + if (GetWindowStyle() & wxSL_LABELS) + { + // make sure we don't allow the entire control to be resized accidently + if (width == GetSize().x) + m_minWidth = -1; + } + + // If the control has labels, we still need to call this again because + // the labels alter the control's w and h values. + wxControl::DoSetSize( x, y, w, h, sizeFlags ); + + m_minWidth = minWidth; +} + +void wxSlider::DoMoveWindow(int x, int y, int width, int height) +{ + wxControl::DoMoveWindow( x, y, width, height ); +} + +// Common processing to invert slider values based on wxSL_INVERSE +int wxSlider::ValueInvertOrNot(int value) const +{ + int result = 0; + + if (m_windowStyle & wxSL_VERTICAL) + { + // The reason for the backwards logic is that Mac's vertical sliders are + // inverted compared to Windows and GTK, hence we want inversion to be the + // default, and if wxSL_INVERSE is set, then we do not invert (use native) + if (m_windowStyle & wxSL_INVERSE) + result = value; + else + result = (m_rangeMax + m_rangeMin) - value; + } + else // normal logic applies to HORIZONTAL sliders + { + result = wxSliderBase::ValueInvertOrNot(value); + } + + return result; +} + +#endif // wxUSE_SLIDER diff --git a/src/osx/carbon/sound.cpp b/src/osx/carbon/sound.cpp new file mode 100644 index 0000000000..b3ff384fc8 --- /dev/null +++ b/src/osx/carbon/sound.cpp @@ -0,0 +1,476 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/sound.cpp +// Purpose: wxSound class implementation: optional +// Author: Ryan Norton +// Modified by: Stefan Csomor +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_SOUND + +#include "wx/sound.h" + +#ifndef WX_PRECOMP + #include "wx/object.h" + #include "wx/string.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/timer.h" +#endif + +#include "wx/file.h" + +// Carbon QT Implementation Details - +// +// Memory: +// 1) OpenDefaultComponent(MovieImportType, kQTFileTypeWave); +// 2) NewMovie(0); +// 3) MovieImportDataRef() //Pass Memory Location to this +// 4) PlayMovie(); +// 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime +// +// File: +// 1) Path as CFString +// 2) Call QTNewDataReferenceFromFullPathCFString +// 3) Call NewMovieFromDataRef +// 4) Call CloseMovieFile +// 4) PlayMovie(); +// 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime +// + +#ifdef __WXMAC__ +#include "wx/mac/uma.h" +#ifndef __DARWIN__ +#include +#include +#endif +#endif + +#include + +//quicktime media layer only required for mac emulation on pc +#ifndef __WXMAC__ +#include +#endif + +#ifndef __DARWIN__ +#include +#else +#include +#endif + +//Time between timer calls +#define MOVIE_DELAY 100 + +static wxTimer* lastSoundTimer=NULL; +static bool lastSoundIsPlaying=false; + +#if !defined(__LP64__) +#define USE_QUICKTIME 1 +#else +#define USE_QUICKTIME 0 +#endif + +#if USE_QUICKTIME +// ------------------------------------------------------------------ +// wxQTTimer - Handle Asyncronous Playing +// ------------------------------------------------------------------ +class wxQTTimer : public wxTimer +{ +public: + wxQTTimer(Movie movie, bool bLoop, bool* playing) : + m_movie(movie), m_bLoop(bLoop), m_pbPlaying(playing) + { + } + + virtual ~wxQTTimer() + { + if(m_pbPlaying) + *m_pbPlaying = false; + + StopMovie(m_movie); + DisposeMovie(m_movie); + Stop(); + + //Note that ExitMovies() is not necessary, but + //the docs are fuzzy on whether or not TerminateQTML is + ExitMovies(); + +#ifndef __WXMAC__ + TerminateQTML(); +#endif + } + + void Shutdown() + { + delete this; + } + + void Notify() + { + if (m_pbPlaying && !*m_pbPlaying) + { + Shutdown(); + } + + if(IsMovieDone(m_movie)) + { + if (!m_bLoop) + Shutdown(); + else + { + StopMovie(m_movie); + GoToBeginningOfMovie(m_movie); + StartMovie(m_movie); + } + } + else + MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie + } + + + Movie& GetMovie() {return m_movie;} + +protected: + Movie m_movie; + bool m_bLoop; + +public: + bool* m_pbPlaying; + +}; + + +class wxSMTimer : public wxTimer +{ +public: + wxSMTimer(void* hSnd, void* pSndChannel, bool bLoop, bool* playing) + : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop), m_pbPlaying(playing) + { + } + + virtual ~wxSMTimer() + { + if(m_pbPlaying) + *m_pbPlaying = false; + SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE); + Stop(); + } + + void Notify() + { + if (m_pbPlaying && !*m_pbPlaying) + { + Shutdown(); + } + + SCStatus stat; + + if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0) + Shutdown(); + + //if the sound isn't playing anymore, see if it's looped, + //and if so play it again, otherwise close things up + if (stat.scChannelBusy == FALSE) + { + if (m_bLoop) + { + if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr) + Shutdown(); + } + else + Shutdown(); + } + } + + void Shutdown() + { + delete this; + } + + void* GetChannel() {return m_pSndChannel;} + +protected: + void* m_hSnd; + void* m_pSndChannel; + bool m_bLoop; + +public: + bool* m_pbPlaying; +}; + +// ------------------------------------------------------------------ +// wxSound +// ------------------------------------------------------------------ + +//Determines whether version 4 of QT is installed +Boolean wxIsQuickTime4Installed (void) +{ +#ifdef __WXMAC__ + short error; + long result; + + error = Gestalt (gestaltQuickTime, &result); + return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400); +#else + return true; +#endif +} + +inline bool wxInitQT () +{ + if (wxIsQuickTime4Installed()) + { + #ifndef __WXMAC__ + int nError; + //-2093 no dll + if ((nError = InitializeQTML(0)) != noErr) + wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError)); + #endif + EnterMovies(); + return true; + } + else + { + wxLogSysError(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4.")); + return false; + } +} + +#endif + +wxSound::wxSound() +: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE) +{ +} + +wxSound::wxSound(const wxString& sFileName, bool isResource) +: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE) +{ + Create(sFileName, isResource); +} + +wxSound::wxSound(int size, const wxByte* data) +: m_hSnd((char*)data), m_waveLength(size), m_pTimer(NULL), m_type(wxSound_MEMORY) +{ +} + +wxSound::~wxSound() +{ +} + +bool wxSound::Create(const wxString& fileName, bool isResource) +{ + Stop(); + + if (isResource) + { +#ifdef __WXMAC__ + m_type = wxSound_RESOURCE; + + Str255 lpSnd ; + + wxMacStringToPascal( fileName , lpSnd ) ; + + m_sndname = fileName; + m_hSnd = (char*) GetNamedResource('snd ', (const unsigned char *) lpSnd); +#else + return false; +#endif + } + else + { + m_type = wxSound_FILE; + m_sndname = fileName; + } + + return true; +} + +bool wxSound::DoPlay(unsigned flags) const +{ + Stop(); + +#if USE_QUICKTIME + + Movie movie; + + switch(m_type) + { + case wxSound_MEMORY: + { + if (!wxInitQT()) + return false; + Handle myHandle, dataRef = nil; + MovieImportComponent miComponent; + Track targetTrack = nil; + TimeValue addedDuration = 0; + long outFlags = 0; + OSErr err; + ComponentResult result; + + myHandle = NewHandleClear((Size)m_waveLength); + + BlockMove(m_hSnd, *myHandle, m_waveLength); + + err = PtrToHand(&myHandle, &dataRef, sizeof(Handle)); + + if (memcmp(&m_hSnd[8], "WAVE", 4) == 0) + miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeWave); + else if (memcmp(&m_hSnd[8], "AIFF", 4) == 0) + miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFF); + else if (memcmp(&m_hSnd[8], "AIFC", 4) == 0) + miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFC); + else + { + wxLogSysError(wxT("wxSound - Location in memory does not contain valid data")); + return false; + } + + movie = NewMovie(0); + + result = MovieImportDataRef(miComponent, dataRef, + HandleDataHandlerSubType, movie, + nil, &targetTrack, + nil, &addedDuration, + movieImportCreateTrack, &outFlags); + + if (result != noErr) + { + wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result)); + } + + SetMovieVolume(movie, kFullVolume); + GoToBeginningOfMovie(movie); + + DisposeHandle(myHandle); + } + break; + case wxSound_RESOURCE: + { + SoundComponentData data; + unsigned long numframes, offset; + + ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset); + //m_waveLength = numFrames * data.numChannels; + + SndChannelPtr pSndChannel; + SndNewChannel(&pSndChannel, sampledSynth, + initNoInterp + + (data.numChannels == 1 ? initMono : initStereo), NULL); + + if(SndPlay(pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr) + return false; + + if (flags & wxSOUND_ASYNC) + { + lastSoundTimer = ((wxSMTimer*&)m_pTimer) + = new wxSMTimer(pSndChannel, m_hSnd, flags & wxSOUND_LOOP ? 1 : 0, + &lastSoundIsPlaying); + lastSoundIsPlaying = true; + + ((wxTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); + } + else + SndDisposeChannel(pSndChannel, TRUE); + + return true; + } + break; + case wxSound_FILE: + { + if (!wxInitQT()) + return false; + + OSErr err = noErr ; + + Handle dataRef = NULL; + OSType dataRefType; + + err = QTNewDataReferenceFromFullPathCFString(wxCFStringRef(m_sndname,wxLocale::GetSystemEncoding()), + (UInt32)kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType); + + wxASSERT(err == noErr); + + if (NULL != dataRef || err != noErr) + { + err = NewMovieFromDataRef( &movie, newMovieDontAskUnresolvedDataRefs , NULL, dataRef, dataRefType ); + wxASSERT(err == noErr); + DisposeHandle(dataRef); + } + + if (err != noErr) + { + wxLogSysError( + wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname.c_str(), err ) + ); + return false; + } + } + break; + default: + return false; + }//end switch(m_type) + + //Start the movie! + StartMovie(movie); + + if (flags & wxSOUND_ASYNC) + { + //Start timer and play movie asyncronously + lastSoundTimer = ((wxQTTimer*&)m_pTimer) = + new wxQTTimer(movie, flags & wxSOUND_LOOP ? 1 : 0, + &lastSoundIsPlaying); + lastSoundIsPlaying = true; + ((wxQTTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); + } + else + { + wxASSERT_MSG(!(flags & wxSOUND_LOOP), wxT("Can't loop and play syncronously at the same time")); + + //Play movie until it ends, then exit + //Note that due to quicktime caching this may not always + //work 100% correctly + while (!IsMovieDone(movie)) + MoviesTask(movie, 1); + + DisposeMovie(movie); + } +#endif + + return true; +} + +bool wxSound::IsPlaying() +{ + return lastSoundIsPlaying; +} + +void wxSound::Stop() +{ + if (lastSoundIsPlaying) + { + delete (wxTimer*&) lastSoundTimer; + lastSoundIsPlaying = false; + lastSoundTimer = NULL; + } +} + +void* wxSound::GetHandle() +{ +#if USE_QUICKTIME + if(m_type == wxSound_RESOURCE) + return (void*) ((wxSMTimer*)m_pTimer)->GetChannel(); + + return (void*) ((wxQTTimer*) m_pTimer)->GetMovie(); +#endif + return NULL; +} + +#endif //wxUSE_SOUND diff --git a/src/osx/carbon/spinbutt.cpp b/src/osx/carbon/spinbutt.cpp new file mode 100644 index 0000000000..02ed2e9423 --- /dev/null +++ b/src/osx/carbon/spinbutt.cpp @@ -0,0 +1,208 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: spinbutt.cpp +// Purpose: wxSpinButton +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_SPINBTN + +#include "wx/spinbutt.h" +#include "wx/mac/uma.h" + + +IMPLEMENT_DYNAMIC_CLASS(wxSpinButton, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxSpinEvent, wxScrollEvent) + + +wxSpinButton::wxSpinButton() + : wxSpinButtonBase() +{ +} + +bool wxSpinButton::Create( wxWindow *parent, + wxWindowID id, const wxPoint& pos, const wxSize& size, + long style, const wxString& name ) +{ + m_macIsUserPane = false; + + if ( !wxSpinButtonBase::Create( parent, id, pos, size, style, wxDefaultValidator, name ) ) + return false; + + m_min = 0; + m_max = 100; + + if (!parent) + return false; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ); + + m_peer = new wxMacControl( this ); + OSStatus err = CreateLittleArrowsControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, 0, m_min, m_max, 1, + m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + m_peer->SetActionProc( GetwxMacLiveScrollbarActionProc() ); + MacPostControlCreate( pos, size ); + + return true; +} + +wxSpinButton::~wxSpinButton() +{ +} + +int wxSpinButton::GetMin() const +{ + return m_min; +} + +int wxSpinButton::GetMax() const +{ + return m_max; +} + +int wxSpinButton::GetValue() const +{ + int n = m_value; + + if (n < m_min) + n = m_min; + else if (n > m_max) + n = m_max; + + return n; +} + +void wxSpinButton::SetValue(int val) +{ + m_value = val; +} + +void wxSpinButton::SetRange(int minVal, int maxVal) +{ + m_min = minVal; + m_max = maxVal; + m_peer->SetMaximum( maxVal ); + m_peer->SetMinimum( minVal ); +} + +void wxSpinButton::MacHandleValueChanged( int inc ) +{ + wxEventType scrollEvent = wxEVT_NULL; + int oldValue = m_value; + + m_value = oldValue + inc; + + if (m_value < m_min) + { + if ( m_windowStyle & wxSP_WRAP ) + m_value = m_max; + else + m_value = m_min; + } + + if (m_value > m_max) + { + if ( m_windowStyle & wxSP_WRAP ) + m_value = m_min; + else + m_value = m_max; + } + + if ( m_value - oldValue == -1 ) + scrollEvent = wxEVT_SCROLL_LINEDOWN; + else if ( m_value - oldValue == 1 ) + scrollEvent = wxEVT_SCROLL_LINEUP; + else + scrollEvent = wxEVT_SCROLL_THUMBTRACK; + + // Do not send an event if the value has not actually changed + // (Also works for wxSpinCtrl) + if ( m_value == oldValue ) + return; + + wxSpinEvent event( scrollEvent, m_windowId ); + + event.SetPosition( m_value ); + event.SetEventObject( this ); + if ((HandleWindowEvent( event )) && !event.IsAllowed()) + m_value = oldValue; + + m_peer->SetValue( m_value ); + + // always send a thumbtrack event + if (scrollEvent != wxEVT_SCROLL_THUMBTRACK) + { + scrollEvent = wxEVT_SCROLL_THUMBTRACK; + wxSpinEvent event2( scrollEvent, GetId() ); + event2.SetPosition( m_value ); + event2.SetEventObject( this ); + HandleWindowEvent( event2 ); + } +} + +void wxSpinButton::MacHandleControlClick(WXWidget WXUNUSED(control), + wxInt16 controlpart, + bool WXUNUSED(mouseStillDown)) +{ + int nScrollInc = 0; + + switch ( controlpart ) + { + case kControlUpButtonPart : + nScrollInc = 1; + break; + + case kControlDownButtonPart : + nScrollInc = -1; + break; + + default: + break; + } + + MacHandleValueChanged( nScrollInc ) ; +} + +wxInt32 wxSpinButton::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler), + WXEVENTREF WXUNUSED(event)) +{ +#if 0 + // these have been handled by the live action proc already + int nScrollInc = 0; + wxMacCarbonEvent cEvent( (EventRef)event ); + + switch ( cEvent.GetParameter(kEventParamControlPart, typeControlPartCode) ) + { + case kControlUpButtonPart : + nScrollInc = 1; + break; + + case kControlDownButtonPart : + nScrollInc = -1; + break; + + default : + break; + } + + MacHandleValueChanged( nScrollInc ) ; +#endif + + return noErr; +} + +wxSize wxSpinButton::DoGetBestSize() const +{ + return wxSize( 16, 24 ); +} + +#endif // wxUSE_SPINBTN diff --git a/src/osx/carbon/spinctrl.cpp b/src/osx/carbon/spinctrl.cpp new file mode 100644 index 0000000000..f9b3fb9e95 --- /dev/null +++ b/src/osx/carbon/spinctrl.cpp @@ -0,0 +1,434 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/spinbutt.cpp +// Purpose: wxSpinCtrl +// Author: Robert +// Modified by: Mark Newsam (Based on GTK file) +// RCS-ID: $Id$ +// Copyright: (c) Robert Roebling +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_SPINCTRL + +#include "wx/spinctrl.h" + +#ifndef WX_PRECOMP + #include "wx/textctrl.h" + #include "wx/containr.h" +#endif + +#include "wx/spinbutt.h" + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// the focus rect around a text may have 4 pixels in each direction +// we handle these problems right now in an extended vis region of a window +static const wxCoord TEXTBORDER = 4 ; +// the margin between the text control and the spin +// HIG says 2px between text and stepper control, +// but a value of 3 leads to the same look as the +// spin controls in Apple's apps +static const wxCoord MARGIN = 3; + +// ---------------------------------------------------------------------------- +// wxSpinCtrlText: text control used by spin control +// ---------------------------------------------------------------------------- + +class wxSpinCtrlText : public wxTextCtrl +{ +public: + wxSpinCtrlText(wxSpinCtrl *spin, const wxString& value) + : wxTextCtrl(spin , wxID_ANY, value, wxDefaultPosition, wxSize(40, wxDefaultCoord)) + { + m_spin = spin; + + // remove the default minsize, the spinctrl will have one instead + SetMinSize(wxDefaultSize); + } + + bool ProcessEvent(wxEvent &event) + { + // Hand button down events to wxSpinCtrl. Doesn't work. + if (event.GetEventType() == wxEVT_LEFT_DOWN && m_spin->ProcessEvent( event )) + return true; + + return wxTextCtrl::ProcessEvent( event ); + } + +protected: + void OnKillFocus(wxFocusEvent& WXUNUSED(event)) + { + long l; + if ( !GetValue().ToLong(&l) ) + { + // not a number at all + return; + } + + // is within range + if (l < m_spin->GetMin()) + l = m_spin->GetMin(); + if (l > m_spin->GetMax()) + l = m_spin->GetMax(); + + // Update text control + wxString str; + str.Printf( wxT("%d"), (int)l ); + if (str != GetValue()) + SetValue( str ); + + if (l != m_spin->m_oldValue) + { + // set value in spin button + // does that trigger an event? + m_spin->m_btn->SetValue( l ); + + // if not + wxCommandEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED, m_spin->GetId()); + event.SetEventObject(m_spin); + event.SetInt(l); + m_spin->HandleWindowEvent(event); + + m_spin->m_oldValue = l; + } + } + + void OnTextChange(wxCommandEvent& event) + { + int val; + if ( m_spin->GetTextValue(&val) ) + { + m_spin->GetSpinButton()->SetValue(val); + + // If we're already processing a text update from m_spin, + // don't send it again, since we could end up recursing + // infinitely. + if (event.GetId() == m_spin->GetId()) + { + event.Skip(); + return; + } + + // Send event that the text was manually changed + wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, m_spin->GetId()); + event.SetEventObject(m_spin); + event.SetString(m_spin->GetText()->GetValue()); + event.SetInt(val); + + m_spin->HandleWindowEvent(event); + } + + event.Skip(); + } + +private: + wxSpinCtrl *m_spin; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(wxSpinCtrlText, wxTextCtrl) + EVT_TEXT(wxID_ANY, wxSpinCtrlText::OnTextChange) + EVT_KILL_FOCUS( wxSpinCtrlText::OnKillFocus) +END_EVENT_TABLE() + +// ---------------------------------------------------------------------------- +// wxSpinCtrlButton: spin button used by spin control +// ---------------------------------------------------------------------------- + +class wxSpinCtrlButton : public wxSpinButton +{ +public: + wxSpinCtrlButton(wxSpinCtrl *spin, int style) + : wxSpinButton(spin ) + { + m_spin = spin; + SetWindowStyle(style | wxSP_VERTICAL); + + // TODO: The spin button gets truncated a little bit due to size + // differences so change it's default size a bit. SMALL still gets a + // bit truncated, but MINI seems to be too small... Readdress this + // when the textctrl issues are all sorted out. + //SetWindowVariant(wxWINDOW_VARIANT_SMALL); + + // remove the default minsize, the spinctrl will have one instead + SetMinSize(wxDefaultSize); + } + +protected: + void OnSpinButton(wxSpinEvent& eventSpin) + { + int pos = eventSpin.GetPosition(); + m_spin->SetTextValue(pos); + + wxCommandEvent event(wxEVT_COMMAND_SPINCTRL_UPDATED, m_spin->GetId()); + event.SetEventObject(m_spin); + event.SetInt(pos); + + m_spin->HandleWindowEvent(event); + + m_spin->m_oldValue = pos; + } + +private: + wxSpinCtrl *m_spin; + + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(wxSpinCtrlButton, wxSpinButton) + EVT_SPIN(wxID_ANY, wxSpinCtrlButton::OnSpinButton) +END_EVENT_TABLE() + +IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl, wxControl) + +BEGIN_EVENT_TABLE(wxSpinCtrl, wxControl) + WX_EVENT_TABLE_CONTROL_CONTAINER(wxSpinCtrl) +END_EVENT_TABLE() + +WX_DELEGATE_TO_CONTROL_CONTAINER(wxSpinCtrl, wxControl) + + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxSpinCtrl creation +// ---------------------------------------------------------------------------- + +void wxSpinCtrl::Init() +{ + m_text = NULL; + m_btn = NULL; + WX_INIT_CONTROL_CONTAINER(); +} + +bool wxSpinCtrl::Create(wxWindow *parent, + wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + long style, + int min, + int max, + int initial, + const wxString& name) +{ + m_macIsUserPane = true; + if ( !wxControl::Create(parent, id, pos, size, style, + wxDefaultValidator, name) ) + { + return false; + } + + // the string value overrides the numeric one (for backwards compatibility + // reasons and also because it is simpler to satisfy the string value which + // comes much sooner in the list of arguments and leave the initial + // parameter unspecified) + if ( !value.empty() ) + { + long l; + if ( value.ToLong(&l) ) + initial = l; + } + + wxSize csize = size ; + m_text = new wxSpinCtrlText(this, value); + m_btn = new wxSpinCtrlButton(this, style); + + m_btn->SetRange(min, max); + m_btn->SetValue(initial); + // make it different + m_oldValue = GetMin()-1; + + if ( size.x == wxDefaultCoord ){ + csize.x = m_text->GetSize().x + MARGIN + m_btn->GetSize().x ; + } + + if ( size.y == wxDefaultCoord ) { + csize.y = m_text->GetSize().y + 2 * TEXTBORDER ; //allow for text border highlights + if ( m_btn->GetSize().y > csize.y ) + csize.y = m_btn->GetSize().y ; + } + + //SetSize(csize); + + //MacPostControlCreate(pos, csize); + SetInitialSize(csize); + + return true; +} + +wxSpinCtrl::~wxSpinCtrl() +{ + // delete the controls now, don't leave them alive even though they would + // still be eventually deleted by our parent - but it will be too late, the + // user code expects them to be gone now + delete m_text; + m_text = NULL ; + delete m_btn; + m_btn = NULL ; +} + +// ---------------------------------------------------------------------------- +// geometry +// ---------------------------------------------------------------------------- + +wxSize wxSpinCtrl::DoGetBestSize() const +{ + if (!m_btn || !m_text) + return GetSize(); + + wxSize sizeBtn = m_btn->GetBestSize(), + sizeText = m_text->GetBestSize(); + + sizeText.y += 2 * TEXTBORDER ; + sizeText.x += 2 * TEXTBORDER ; + + int height; + if (sizeText.y > sizeBtn.y) + height = sizeText.y; + else + height = sizeBtn.y; + + return wxSize(sizeBtn.x + sizeText.x + MARGIN, height ); +} + +void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height) +{ + // position the subcontrols inside the client area + wxSize sizeBtn = m_btn->GetSize(); + wxSize sizeText = m_text->GetSize(); + + wxControl::DoMoveWindow(x, y, width, height); + + wxCoord wText = width - sizeBtn.x - MARGIN - 2 * TEXTBORDER; + + m_text->SetSize(TEXTBORDER, (height - sizeText.y) / 2, wText, -1); + m_btn->SetSize(0 + wText + MARGIN + TEXTBORDER , (height - sizeBtn.y) / 2 , -1, -1 ); +} + +// ---------------------------------------------------------------------------- +// operations forwarded to the subcontrols +// ---------------------------------------------------------------------------- + +bool wxSpinCtrl::Enable(bool enable) +{ + if ( !wxControl::Enable(enable) ) + return false; + return true; +} + +bool wxSpinCtrl::Show(bool show) +{ + if ( !wxControl::Show(show) ) + return false; + return true; +} + +// ---------------------------------------------------------------------------- +// value and range access +// ---------------------------------------------------------------------------- + +bool wxSpinCtrl::GetTextValue(int *val) const +{ + long l; + if ( !m_text->GetValue().ToLong(&l) ) + { + // not a number at all + return false; + } + + if ( l < GetMin() || l > GetMax() ) + { + // out of range + return false; + } + + *val = l; + + return true; +} + +int wxSpinCtrl::GetValue() const +{ + return m_btn ? m_btn->GetValue() : 0; +} + +int wxSpinCtrl::GetMin() const +{ + return m_btn ? m_btn->GetMin() : 0; +} + +int wxSpinCtrl::GetMax() const +{ + return m_btn ? m_btn->GetMax() : 0; +} + +// ---------------------------------------------------------------------------- +// changing value and range +// ---------------------------------------------------------------------------- + +void wxSpinCtrl::SetTextValue(int val) +{ + wxCHECK_RET( m_text, _T("invalid call to wxSpinCtrl::SetTextValue") ); + + m_text->SetValue(wxString::Format(_T("%d"), val)); + + // select all text + m_text->SetSelection(0, -1); + + // and give focus to the control! + // m_text->SetFocus(); Why???? TODO. +} + +void wxSpinCtrl::SetValue(int val) +{ + wxCHECK_RET( m_btn, _T("invalid call to wxSpinCtrl::SetValue") ); + + SetTextValue(val); + + m_btn->SetValue(val); + m_oldValue = val; +} + +void wxSpinCtrl::SetValue(const wxString& text) +{ + wxCHECK_RET( m_text, _T("invalid call to wxSpinCtrl::SetValue") ); + + long val; + if ( text.ToLong(&val) && ((val > INT_MIN) && (val < INT_MAX)) ) + { + SetValue((int)val); + } + else // not a number at all or out of range + { + m_text->SetValue(text); + m_text->SetSelection(0, -1); + } +} + +void wxSpinCtrl::SetRange(int min, int max) +{ + wxCHECK_RET( m_btn, _T("invalid call to wxSpinCtrl::SetRange") ); + + m_btn->SetRange(min, max); +} + +void wxSpinCtrl::SetSelection(long from, long to) +{ + // if from and to are both -1, it means (in wxWidgets) that all text should + // be selected + if ( (from == -1) && (to == -1) ) + { + from = 0; + } + m_text->SetSelection(from, to); +} + +#endif // wxUSE_SPINCTRL diff --git a/src/osx/carbon/srchctrl.cpp b/src/osx/carbon/srchctrl.cpp new file mode 100644 index 0000000000..4a78629391 --- /dev/null +++ b/src/osx/carbon/srchctrl.cpp @@ -0,0 +1,398 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/srchctrl.cpp +// Purpose: implements mac carbon wxSearchCtrl +// Author: Vince Harron +// Created: 2006-02-19 +// RCS-ID: $Id$ +// Copyright: Vince Harron +// License: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_SEARCHCTRL + +#include "wx/srchctrl.h" + +#ifndef WX_PRECOMP + #include "wx/menu.h" +#endif //WX_PRECOMP + +#if wxUSE_NATIVE_SEARCH_CONTROL + +#include "wx/mac/uma.h" +#include "wx/mac/carbon/private/mactext.h" + +BEGIN_EVENT_TABLE(wxSearchCtrl, wxSearchCtrlBase) +END_EVENT_TABLE() + +IMPLEMENT_DYNAMIC_CLASS(wxSearchCtrl, wxSearchCtrlBase) + +// ============================================================================ +// wxMacSearchFieldControl +// ============================================================================ + +static const EventTypeSpec eventList[] = +{ + { kEventClassSearchField, kEventSearchFieldCancelClicked } , + { kEventClassSearchField, kEventSearchFieldSearchClicked } , +}; + +class wxMacSearchFieldControl : public wxMacUnicodeTextControl +{ +public : + wxMacSearchFieldControl( wxTextCtrl *wxPeer, + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) : wxMacUnicodeTextControl( wxPeer ) + { + Create( wxPeer, str, pos, size, style ); + } + + // search field options + virtual void ShowSearchButton( bool show ); + virtual bool IsSearchButtonVisible() const; + + virtual void ShowCancelButton( bool show ); + virtual bool IsCancelButtonVisible() const; + + virtual void SetSearchMenu( wxMenu* menu ); + virtual wxMenu* GetSearchMenu() const; + + virtual void SetDescriptiveText(const wxString& text); + virtual wxString GetDescriptiveText() const; + +protected : + virtual void CreateControl( wxTextCtrl* peer, const Rect* bounds, CFStringRef crf ); + +private: + wxMenu* m_menu; +} ; + +void wxMacSearchFieldControl::CreateControl(wxTextCtrl* WXUNUSED(peer), + const Rect* bounds, + CFStringRef WXUNUSED(crf)) +{ + OptionBits attributes = kHISearchFieldAttributesSearchIcon; + + HIRect hibounds = { { bounds->left, bounds->top }, { bounds->right-bounds->left, bounds->bottom-bounds->top } }; + verify_noerr( HISearchFieldCreate( + &hibounds, + attributes, + 0, // MenuRef + CFSTR("Search"), + &m_controlRef + ) ); + HIViewSetVisible (m_controlRef, true); +} + +// search field options +void wxMacSearchFieldControl::ShowSearchButton( bool show ) +{ + OptionBits set = 0; + OptionBits clear = 0; + if ( show ) + { + set |= kHISearchFieldAttributesSearchIcon; + } + else + { + clear |= kHISearchFieldAttributesSearchIcon; + } + HISearchFieldChangeAttributes( m_controlRef, set, clear ); +} + +bool wxMacSearchFieldControl::IsSearchButtonVisible() const +{ + OptionBits attributes = 0; + verify_noerr( HISearchFieldGetAttributes( m_controlRef, &attributes ) ); + return ( attributes & kHISearchFieldAttributesSearchIcon ) != 0; +} + +void wxMacSearchFieldControl::ShowCancelButton( bool show ) +{ + OptionBits set = 0; + OptionBits clear = 0; + if ( show ) + { + set |= kHISearchFieldAttributesCancel; + } + else + { + clear |= kHISearchFieldAttributesCancel; + } + HISearchFieldChangeAttributes( m_controlRef, set, clear ); +} + +bool wxMacSearchFieldControl::IsCancelButtonVisible() const +{ + OptionBits attributes = 0; + verify_noerr( HISearchFieldGetAttributes( m_controlRef, &attributes ) ); + return ( attributes & kHISearchFieldAttributesCancel ) != 0; +} + +void wxMacSearchFieldControl::SetSearchMenu( wxMenu* menu ) +{ + m_menu = menu; + if ( m_menu ) + { + verify_noerr( HISearchFieldSetSearchMenu( m_controlRef, MAC_WXHMENU(m_menu->GetHMenu()) ) ); + } + else + { + verify_noerr( HISearchFieldSetSearchMenu( m_controlRef, 0 ) ); + } +} + +wxMenu* wxMacSearchFieldControl::GetSearchMenu() const +{ + return m_menu; +} + + +void wxMacSearchFieldControl::SetDescriptiveText(const wxString& text) +{ + verify_noerr( HISearchFieldSetDescriptiveText( + m_controlRef, + wxCFStringRef( text, wxFont::GetDefaultEncoding() ))); +} + +wxString wxMacSearchFieldControl::GetDescriptiveText() const +{ + CFStringRef cfStr; + verify_noerr( HISearchFieldCopyDescriptiveText( m_controlRef, &cfStr )); + if ( cfStr ) + { + return wxCFStringRef(cfStr).AsString(); + } + else + { + return wxEmptyString; + } +} + +// ============================================================================ +// implementation +// ============================================================================ + +static pascal OSStatus wxMacSearchControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + + wxMacCarbonEvent cEvent( event ) ; + + ControlRef controlRef ; + wxSearchCtrl* thisWindow = (wxSearchCtrl*) data ; + cEvent.GetParameter( kEventParamDirectObject , &controlRef ) ; + + switch( GetEventKind( event ) ) + { + case kEventSearchFieldCancelClicked : + thisWindow->MacSearchFieldCancelHit( handler , event ) ; + break ; + case kEventSearchFieldSearchClicked : + thisWindow->MacSearchFieldSearchHit( handler , event ) ; + break ; + } + + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacSearchControlEventHandler ) + + +// ---------------------------------------------------------------------------- +// wxSearchCtrl creation +// ---------------------------------------------------------------------------- + +// creation +// -------- + +wxSearchCtrl::wxSearchCtrl() +{ + Init(); +} + +wxSearchCtrl::wxSearchCtrl(wxWindow *parent, wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + Init(); + + Create(parent, id, value, pos, size, style, validator, name); +} + +void wxSearchCtrl::Init() +{ + m_menu = 0; +} + +bool wxSearchCtrl::Create(wxWindow *parent, wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + if ( !wxTextCtrl::Create(parent, id, wxEmptyString, pos, size, wxBORDER_NONE | style, validator, name) ) + { + return false; + } + + EventHandlerRef searchEventHandler; + InstallControlEventHandler( m_peer->GetControlRef(), GetwxMacSearchControlEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, this, + (EventHandlerRef *)&searchEventHandler); + + SetValue(value); + + return true; +} + +wxSearchCtrl::~wxSearchCtrl() +{ + delete m_menu; +} + +wxSize wxSearchCtrl::DoGetBestSize() const +{ + wxSize size = wxWindow::DoGetBestSize(); + // it seems to return a default width of about 16, which is way too small here. + if (size.GetWidth() < 100) + size.SetWidth(100); + + return size; +} + +void wxSearchCtrl::SetFocus() +{ + // NB: We have to implement SetFocus a little differently because kControlFocusNextPart + // leads to setting the focus on the search icon rather than the text area. + // We get around this by explicitly telling the control to set focus to the + // text area. + if ( !AcceptsFocus() ) + return ; + + wxWindow* former = FindFocus() ; + if ( former == this ) + return ; + + // as we cannot rely on the control features to find out whether we are in full keyboard mode, + // we can only leave in case of an error + OSStatus err = m_peer->SetFocus( kControlEditTextPart ) ; + if ( err == errCouldntSetFocus ) + return ; + + SetUserFocusWindow( (WindowRef)MacGetTopLevelWindowRef() ); +} + +// search control specific interfaces +// wxSearchCtrl owns menu after this call +void wxSearchCtrl::SetMenu( wxMenu* menu ) +{ + if ( menu == m_menu ) + { + // no change + return; + } + + if ( m_menu ) + { + m_menu->SetInvokingWindow( 0 ); + } + + delete m_menu; + m_menu = menu; + + if ( m_menu ) + { + m_menu->SetInvokingWindow( this ); + } + + GetPeer()->SetSearchMenu( m_menu ); +} + +wxMenu* wxSearchCtrl::GetMenu() +{ + return m_menu; +} + +void wxSearchCtrl::ShowSearchButton( bool show ) +{ + if ( IsSearchButtonVisible() == show ) + { + // no change + return; + } + GetPeer()->ShowSearchButton( show ); +} + +bool wxSearchCtrl::IsSearchButtonVisible() const +{ + return GetPeer()->IsSearchButtonVisible(); +} + + +void wxSearchCtrl::ShowCancelButton( bool show ) +{ + if ( IsCancelButtonVisible() == show ) + { + // no change + return; + } + GetPeer()->ShowCancelButton( show ); +} + +bool wxSearchCtrl::IsCancelButtonVisible() const +{ + return GetPeer()->IsCancelButtonVisible(); +} + +void wxSearchCtrl::SetDescriptiveText(const wxString& text) +{ + GetPeer()->SetDescriptiveText(text); +} + +wxString wxSearchCtrl::GetDescriptiveText() const +{ + return GetPeer()->GetDescriptiveText(); +} + +wxInt32 wxSearchCtrl::MacSearchFieldSearchHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCommandEvent event(wxEVT_COMMAND_SEARCHCTRL_SEARCH_BTN, m_windowId ); + event.SetEventObject(this); + ProcessCommand(event); + return eventNotHandledErr ; +} + +wxInt32 wxSearchCtrl::MacSearchFieldCancelHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCommandEvent event(wxEVT_COMMAND_SEARCHCTRL_CANCEL_BTN, m_windowId ); + event.SetEventObject(this); + ProcessCommand(event); + return eventNotHandledErr ; +} + + +void wxSearchCtrl::CreatePeer( + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) +{ + m_peer = new wxMacSearchFieldControl( this , str , pos , size , style ); +} + +#endif // wxUSE_NATIVE_SEARCH_CONTROL + +#endif // wxUSE_SEARCHCTRL diff --git a/src/osx/carbon/statbmp.cpp b/src/osx/carbon/statbmp.cpp new file mode 100644 index 0000000000..8948b0c3e5 --- /dev/null +++ b/src/osx/carbon/statbmp.cpp @@ -0,0 +1,86 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/statbmp.cpp +// Purpose: wxStaticBitmap +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_STATBMP + +#include "wx/statbmp.h" + +#ifndef WX_PRECOMP + #include "wx/dcclient.h" +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxStaticBitmap, wxControl) + +/* + * wxStaticBitmap + */ + +BEGIN_EVENT_TABLE(wxStaticBitmap, wxStaticBitmapBase) + EVT_PAINT(wxStaticBitmap::OnPaint) +END_EVENT_TABLE() + +bool wxStaticBitmap::Create(wxWindow *parent, wxWindowID id, + const wxBitmap& bitmap, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) +{ + SetName(name); + + m_backgroundColour = parent->GetBackgroundColour() ; + m_foregroundColour = parent->GetForegroundColour() ; + + m_bitmap = bitmap; + if ( id == wxID_ANY ) + m_windowId = (int)NewControlId(); + else + m_windowId = id; + + m_windowStyle = style; + + bool ret = wxControl::Create( parent, id, pos, size, style , wxDefaultValidator , name ); + SetInitialSize( size ) ; + + return ret; +} + +void wxStaticBitmap::SetBitmap(const wxBitmap& bitmap) +{ + m_bitmap = bitmap; + InvalidateBestSize(); + SetSize(GetBestSize()); + Refresh() ; +} + +void wxStaticBitmap::OnPaint( wxPaintEvent& WXUNUSED(event) ) +{ + wxPaintDC dc(this); + PrepareDC(dc); + + if (m_bitmap.Ok()) + { + dc.DrawBitmap( m_bitmap , 0 , 0 , TRUE ) ; + } +} + +wxSize wxStaticBitmap::DoGetBestSize() const +{ + if ( m_bitmap.Ok() ) + return DoGetSizeFromClientSize( wxSize(m_bitmap.GetWidth(), m_bitmap.GetHeight()) ); + + // this is completely arbitrary + return DoGetSizeFromClientSize( wxSize(16, 16) ); +} + +#endif diff --git a/src/osx/carbon/statbox.cpp b/src/osx/carbon/statbox.cpp new file mode 100644 index 0000000000..398fc5fcb2 --- /dev/null +++ b/src/osx/carbon/statbox.cpp @@ -0,0 +1,76 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: statbox.cpp +// Purpose: wxStaticBox +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_STATBOX + +#include "wx/statbox.h" +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxStaticBox, wxControl) + + +bool wxStaticBox::Create( wxWindow *parent, + wxWindowID id, + const wxString& label, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + m_macIsUserPane = false; + + if ( !wxControl::Create( parent, id, pos, size, style, wxDefaultValidator, name ) ) + return false; + + m_labelOrig = m_label = label; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + + m_peer = new wxMacControl( this ); + OSStatus err = CreateGroupBoxControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, CFSTR(""), + true /*primary*/, m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + MacPostControlCreate( pos, size ); + + return true; +} + +void wxStaticBox::GetBordersForSizer(int *borderTop, int *borderOther) const +{ + static int extraTop = -1; // Uninitted + static int other = 5; + + if ( extraTop == -1 ) + { + // The minimal border used for the top. + // Later on, the staticbox's font height is added to this. + extraTop = 0; + + // As indicated by the HIG, Panther needs an extra border of 11 + // pixels (otherwise overlapping occurs at the top). The "other" + // border has to be 11. + extraTop = 11; + other = 11; + } + + *borderTop = extraTop; + if ( !m_label.empty() ) + *borderTop += GetCharHeight(); + + *borderOther = other; +} + +#endif // wxUSE_STATBOX + diff --git a/src/osx/carbon/statbrma.cpp b/src/osx/carbon/statbrma.cpp new file mode 100644 index 0000000000..926198c3b0 --- /dev/null +++ b/src/osx/carbon/statbrma.cpp @@ -0,0 +1,181 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/statbarma.cpp +// Purpose: native implementation of wxStatusBar (optional) +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) 1998 Stefan Csomor +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_STATUSBAR + +#include "wx/statusbr.h" + +#ifndef WX_PRECOMP + #include "wx/dc.h" + #include "wx/dcclient.h" + #include "wx/toplevel.h" +#endif + +#include "wx/mac/private.h" + + +BEGIN_EVENT_TABLE(wxStatusBarMac, wxStatusBarGeneric) + EVT_PAINT(wxStatusBarMac::OnPaint) +END_EVENT_TABLE() + + +wxStatusBarMac::wxStatusBarMac(wxWindow *parent, + wxWindowID id, + long style, + const wxString& name) + : + wxStatusBarGeneric() +{ + SetParent( NULL ); + Create( parent, id, style, name ); +} + +wxStatusBarMac::wxStatusBarMac() + : + wxStatusBarGeneric() +{ + SetParent( NULL ); +} + +wxStatusBarMac::~wxStatusBarMac() +{ +} + +bool wxStatusBarMac::Create(wxWindow *parent, wxWindowID id, + long style , + const wxString& name) +{ + if ( !wxStatusBarGeneric::Create( parent, id, style, name ) ) + return false; + + if ( parent->MacGetTopLevelWindow()->MacGetMetalAppearance() ) + SetBackgroundStyle( wxBG_STYLE_TRANSPARENT ); + + // normal system font is too tall for fitting into the standard height + SetWindowVariant( wxWINDOW_VARIANT_SMALL ); + + return true; +} + +void wxStatusBarMac::DrawFieldText(wxDC& dc, int i) +{ + int w, h; + GetSize( &w , &h ); + wxRect rect; + GetFieldRect( i, rect ); + + if ( !MacIsReallyHilited() ) + dc.SetTextForeground( wxColour( 0x80, 0x80, 0x80 ) ); + + wxString text(GetStatusText( i )); + + wxCoord x, y; + dc.GetTextExtent(text, &x, &y); + + int leftMargin = 2; + int xpos = rect.x + leftMargin + 1; + int ypos = 1; + + if ( MacGetTopLevelWindow()->MacGetMetalAppearance() ) + ypos++; + + dc.SetClippingRegion(rect.x, 0, rect.width, h); + + dc.DrawText(text, xpos, ypos); + + dc.DestroyClippingRegion(); +} + +void wxStatusBarMac::DrawField(wxDC& dc, int i) +{ + DrawFieldText(dc, i); +} + +void wxStatusBarMac::SetStatusText(const wxString& text, int number) +{ + wxCHECK_RET( (number >= 0) && (number < m_nFields), + wxT("invalid status bar field index") ); + + if ( m_statusStrings[number] == text ) + return ; + + m_statusStrings[number] = text; + wxRect rect; + GetFieldRect(number, rect); + int w, h; + GetSize( &w, &h ); + rect.y = 0; + rect.height = h ; + Refresh( true, &rect ); + Update(); +} + +void wxStatusBarMac::OnPaint(wxPaintEvent& WXUNUSED(event)) +{ + wxPaintDC dc(this); + dc.Clear(); + + int major, minor; + wxGetOsVersion( &major, &minor ); + int w, h; + GetSize( &w, &h ); + + if ( MacIsReallyHilited() ) + { + wxPen white( *wxWHITE , 1 , wxSOLID ); + if (major >= 10) + { + // Finder statusbar border color: (Project Builder similar is 9B9B9B) + if ( MacGetTopLevelWindow()->MacGetMetalAppearance() ) + dc.SetPen(wxPen(wxColour(0x40, 0x40, 0x40), 1, wxSOLID)); + else + dc.SetPen(wxPen(wxColour(0xB1, 0xB1, 0xB1), 1, wxSOLID)); + } + else + { + wxPen black( *wxBLACK , 1 , wxSOLID ); + dc.SetPen(black); + } + + dc.DrawLine(0, 0, w, 0); + dc.SetPen(white); + dc.DrawLine(0, 1, w, 1); + } + else + { + if (major >= 10) + // Finder statusbar border color: (Project Builder similar is 9B9B9B) + dc.SetPen(wxPen(wxColour(0xB1, 0xB1, 0xB1), 1, wxSOLID)); + else + dc.SetPen(wxPen(wxColour(0x80, 0x80, 0x80), 1, wxSOLID)); + + dc.DrawLine(0, 0, w, 0); + } + + int i; + if ( GetFont().Ok() ) + dc.SetFont(GetFont()); + dc.SetBackgroundMode(wxTRANSPARENT); + + for ( i = 0; i < m_nFields; i ++ ) + DrawField(dc, i); +} + +void wxStatusBarMac::MacHiliteChanged() +{ + Refresh(); + Update(); +} + +#endif // wxUSE_STATUSBAR + diff --git a/src/osx/carbon/statline.cpp b/src/osx/carbon/statline.cpp new file mode 100644 index 0000000000..62abfd106a --- /dev/null +++ b/src/osx/carbon/statline.cpp @@ -0,0 +1,44 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/statline.cpp +// Purpose: wxStaticLine class +// Author: Vadim Zeitlin +// Created: 28.06.99 +// Version: $Id$ +// Copyright: (c) 1998 Vadim Zeitlin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/statline.h" + +#ifndef WX_PRECOMP + #include "wx/statbox.h" +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxStaticLine, wxControl) + + +bool wxStaticLine::Create( wxWindow *parent, + wxWindowID id, + const wxPoint &pos, + const wxSize &size, + long style, + const wxString &name ) +{ + if ( !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ) ) + return false; + + // this is ugly but it's better than nothing: + // use a thin static box to emulate static line + + wxSize sizeReal = AdjustSize( size ); + +// m_statbox = new wxStaticBox( parent, id, wxT(""), pos, sizeReal, style, name ); + + return true; +} diff --git a/src/osx/carbon/statlmac.cpp b/src/osx/carbon/statlmac.cpp new file mode 100644 index 0000000000..d0102cc8c3 --- /dev/null +++ b/src/osx/carbon/statlmac.cpp @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/statlmac.cpp +// Purpose: a generic wxStaticLine class +// Author: Vadim Zeitlin +// Created: 28.06.99 +// Version: $Id$ +// Copyright: (c) 1998 Vadim Zeitlin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_STATLINE + +#include "wx/statline.h" + +#ifndef WX_PRECOMP + #include "wx/statbox.h" +#endif + +#include "wx/mac/uma.h" + +// ============================================================================ +// implementation +// ============================================================================ + +IMPLEMENT_DYNAMIC_CLASS(wxStaticLine, wxControl) + +// ---------------------------------------------------------------------------- +// wxStaticLine +// ---------------------------------------------------------------------------- + +bool wxStaticLine::Create( wxWindow *parent, + wxWindowID id, + const wxPoint &pos, + const wxSize &size, + long style, + const wxString &name) +{ + m_macIsUserPane = false ; + + if ( !wxStaticLineBase::Create(parent, id, pos, size, + style, wxDefaultValidator, name) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ; + m_peer = new wxMacControl(this) ; + verify_noerr(CreateSeparatorControl(MAC_WXHWND(parent->MacGetTopLevelWindowRef()),&bounds, m_peer->GetControlRefAddr() ) ) ; + + MacPostControlCreate(pos,size) ; + + return true; +} + +#endif //wxUSE_STATLINE diff --git a/src/osx/carbon/stattext.cpp b/src/osx/carbon/stattext.cpp new file mode 100644 index 0000000000..d0285e6b06 --- /dev/null +++ b/src/osx/carbon/stattext.cpp @@ -0,0 +1,197 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/stattext.cpp +// Purpose: wxStaticText +// Author: Stefan Csomor +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_STATTEXT + +#include "wx/stattext.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/dc.h" + #include "wx/dcclient.h" + #include "wx/settings.h" +#endif // WX_PRECOMP + +#include "wx/notebook.h" +#include "wx/tabctrl.h" + +#include "wx/mac/uma.h" + +#include + +IMPLEMENT_DYNAMIC_CLASS(wxStaticText, wxControl) + + +bool wxStaticText::Create( wxWindow *parent, + wxWindowID id, + const wxString& label, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + m_macIsUserPane = false; + + if ( !wxControl::Create( parent, id, pos, size, style, wxDefaultValidator, name ) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + + m_peer = new wxMacControl( this ); + OSStatus err = CreateStaticTextControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), + &bounds, NULL, NULL, m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + if ( ( style & wxST_ELLIPSIZE_END ) || ( style & wxST_ELLIPSIZE_MIDDLE ) ) + { + TruncCode tCode = truncEnd; + if ( style & wxST_ELLIPSIZE_MIDDLE ) + tCode = truncMiddle; + + err = m_peer->SetData( kControlStaticTextTruncTag, tCode ); + err = m_peer->SetData( kControlStaticTextIsMultilineTag, (Boolean)0 ); + } + + MacPostControlCreate( pos, size ); + + SetLabel(label); + + return true; +} + +wxSize wxStaticText::DoGetBestSize() const +{ + Rect bestsize = { 0 , 0 , 0 , 0 } ; + Point bounds; + + // try the built-in best size if available + Boolean former = m_peer->GetData( kControlStaticTextIsMultilineTag); + m_peer->SetData( kControlStaticTextIsMultilineTag, (Boolean)0 ); + m_peer->GetBestRect( &bestsize ) ; + m_peer->SetData( kControlStaticTextIsMultilineTag, former ); + if ( !EmptyRect( &bestsize ) ) + { + bounds.h = bestsize.right - bestsize.left ; + bounds.v = bestsize.bottom - bestsize.top ; + } + else + { + ControlFontStyleRec controlFont; + OSStatus err = m_peer->GetData( kControlEntireControl, kControlFontStyleTag, &controlFont ); + verify_noerr( err ); + + wxCFStringRef str( m_label, GetFont().GetEncoding() ); + +#if wxMAC_USE_ATSU_TEXT + SInt16 baseline; + if ( m_font.MacGetThemeFontID() != kThemeCurrentPortFont ) + { + err = GetThemeTextDimensions( + (!m_label.empty() ? (CFStringRef)str : CFSTR(" ")), + m_font.MacGetThemeFontID(), kThemeStateActive, false, &bounds, &baseline ); + verify_noerr( err ); + } + else +#endif + { + wxClientDC dc(const_cast(this)); + wxCoord width, height ; + dc.GetTextExtent( m_label , &width, &height); + bounds.h = width; + bounds.v = height; + } + + if ( m_label.empty() ) + bounds.h = 0; + } + bounds.h += MacGetLeftBorderSize() + MacGetRightBorderSize(); + bounds.v += MacGetTopBorderSize() + MacGetBottomBorderSize(); + + return wxSize( bounds.h, bounds.v ); +} + +void wxStaticText::SetLabel(const wxString& label) +{ + m_labelOrig = label; + + // middle/end ellipsization is handled by the OS: + if ( HasFlag(wxST_ELLIPSIZE_END) || HasFlag(wxST_ELLIPSIZE_MIDDLE) ) + { + // remove markup + wxString str(label); + if (HasFlag(wxST_MARKUP)) + str = RemoveMarkup(label); + + // and leave ellipsization to the OS + DoSetLabel(str); + } + else // not supported natively + { + DoSetLabel(GetEllipsizedLabelWithoutMarkup()); + } + + if ( !(GetWindowStyle() & wxST_NO_AUTORESIZE) && + !IsEllipsized() ) // don't resize if we adjust to current size + { + InvalidateBestSize(); + SetSize( GetBestSize() ); + } + + Refresh(); + + // we shouldn't need forced updates + // Update(); +} + +bool wxStaticText::SetFont(const wxFont& font) +{ + bool ret = wxControl::SetFont( font ); + + if ( ret ) + { + if ( !(GetWindowStyle() & wxST_NO_AUTORESIZE) ) + { + InvalidateBestSize(); + SetSize( GetBestSize() ); + } + } + + return ret; +} + + +// for wxST_ELLIPSIZE_* support: + +void wxStaticText::DoSetLabel(const wxString& label) +{ + m_labelOrig = label; + m_label = RemoveMnemonics(label); + + wxCFStringRef str( m_label, GetFont().GetEncoding() ); + OSStatus err = m_peer->SetData(kControlEntireControl, kControlStaticTextCFStringTag, str); + verify_noerr( err ); +} + +wxString wxStaticText::DoGetLabel() const +{ + return m_label; +} + +/* + FIXME: UpdateLabel() should be called on size events when wxST_ELLIPSIZE_START is set + to allow correct dynamic ellipsizing of the label +*/ + +#endif //if wxUSE_STATTEXT diff --git a/src/osx/carbon/tabctrl.cpp b/src/osx/carbon/tabctrl.cpp new file mode 100644 index 0000000000..bd557b762d --- /dev/null +++ b/src/osx/carbon/tabctrl.cpp @@ -0,0 +1,202 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/tabctrl.cpp +// Purpose: wxTabCtrl +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_TAB_DIALOG + +#include "wx/tabctrl.h" + +#ifndef WX_PRECOMP + #include "wx/control.h" +#endif + +#include "wx/mac/uma.h" + +IMPLEMENT_DYNAMIC_CLASS(wxTabCtrl, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxTabEvent, wxNotifyEvent) + +DEFINE_EVENT_TYPE(wxEVT_COMMAND_TAB_SEL_CHANGED) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_TAB_SEL_CHANGING) + + +BEGIN_EVENT_TABLE(wxTabCtrl, wxControl) +END_EVENT_TABLE() + + +wxTabCtrl::wxTabCtrl() +{ + m_macIsUserPane = false; + m_imageList = NULL; +} + +bool wxTabCtrl::Create( wxWindow *parent, + wxWindowID id, const wxPoint& pos, const wxSize& size, + long style, const wxString& name ) +{ + m_macIsUserPane = false; + m_imageList = NULL; + + if ( !wxControl::Create( parent, id, pos, size, style, wxDefaultValidator, name ) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this, pos, size ); + + UInt16 tabstyle = kControlTabDirectionNorth; + ControlTabSize tabsize = kControlTabSizeLarge; + if ( GetWindowVariant() == wxWINDOW_VARIANT_SMALL ) + tabsize = kControlTabSizeSmall ; + else if ( GetWindowVariant() == wxWINDOW_VARIANT_MINI ) + { + tabsize = 3 ; + } + + m_peer = new wxMacControl( this ); + OSStatus err = CreateTabsControl( + MAC_WXHWND(parent->MacGetTopLevelWindowRef()), &bounds, + tabsize, tabstyle, 0, NULL, m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + MacPostControlCreate( pos, size ); + + return true; +} + +wxTabCtrl::~wxTabCtrl() +{ +} + +void wxTabCtrl::Command(wxCommandEvent& event) +{ +} + +bool wxTabCtrl::DeleteAllItems() +{ + // TODO: + return false; +} + +bool wxTabCtrl::DeleteItem(int item) +{ + // TODO: + return false; +} + +int wxTabCtrl::GetSelection() const +{ + // TODO: + return 0; +} + +// Get the tab with the current keyboard focus +// +int wxTabCtrl::GetCurFocus() const +{ + // TODO: + return 0; +} + +wxImageList * wxTabCtrl::GetImageList() const +{ + return m_imageList; +} + +int wxTabCtrl::GetItemCount() const +{ + // TODO: + return 0; +} + +// Get the rect corresponding to the tab +bool wxTabCtrl::GetItemRect(int item, wxRect& wxrect) const +{ + // TODO: + return false; +} + +int wxTabCtrl::GetRowCount() const +{ + // TODO: + return 0; +} + +wxString wxTabCtrl::GetItemText(int item) const +{ + // TODO: + return wxEmptyString; +} + +int wxTabCtrl::GetItemImage(int item) const +{ + // TODO: + return 0; +} + +void* wxTabCtrl::GetItemData(int item) const +{ + // TODO: + return NULL; +} + +int wxTabCtrl::HitTest(const wxPoint& pt, long& flags) +{ + // TODO: + return 0; +} + +bool wxTabCtrl::InsertItem(int item, const wxString& text, int imageId, void* data) +{ + // TODO: + return false; +} + +int wxTabCtrl::SetSelection(int item) +{ + // TODO: + return 0; +} + +void wxTabCtrl::SetImageList(wxImageList* imageList) +{ + // TODO: +} + +bool wxTabCtrl::SetItemText(int item, const wxString& text) +{ + // TODO: + return false; +} + +bool wxTabCtrl::SetItemImage(int item, int image) +{ + // TODO: + return false; +} + +bool wxTabCtrl::SetItemData(int item, void* data) +{ + // TODO: + return false; +} + +// Set the size for a fixed-width tab control +void wxTabCtrl::SetItemSize(const wxSize& size) +{ + // TODO: +} + +// Set the padding between tabs +void wxTabCtrl::SetPadding(const wxSize& padding) +{ + // TODO: +} + +#endif // wxUSE_TAB_DIALOG diff --git a/src/osx/carbon/taskbar.cpp b/src/osx/carbon/taskbar.cpp new file mode 100644 index 0000000000..9bb02f5124 --- /dev/null +++ b/src/osx/carbon/taskbar.cpp @@ -0,0 +1,507 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/taskbar.cpp +// Purpose: wxTaskBarIcon +// Author: Ryan Norton +// Modified by: +// Created: 09/25/2004 +// RCS-ID: $Id$ +// Copyright: (c) 2004 Ryan Norton +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_TASKBARICON + +#include "wx/taskbar.h" + +#ifndef WX_PRECOMP + #include "wx/dcmemory.h" + #include "wx/menu.h" + #include "wx/toplevel.h" + #include "wx/icon.h" +#endif + +#include "wx/mac/private.h" + +class wxTaskBarIconImpl +{ +public: + wxTaskBarIconImpl(wxTaskBarIcon* parent); + virtual ~wxTaskBarIconImpl(); + + virtual bool IsIconInstalled() const = 0; + virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip) = 0; + virtual bool RemoveIcon() = 0; + virtual bool PopupMenu(wxMenu *menu) = 0; + + wxMenu * CreatePopupMenu() + { return m_parent->CreatePopupMenu(); } + + wxTaskBarIcon *m_parent; + class wxTaskBarIconWindow *m_menuEventWindow; + + DECLARE_NO_COPY_CLASS(wxTaskBarIconImpl) +}; + +//----------------------------------------------------------------------------- +// +// wxTaskBarIconWindow +// +// Event handler for menus +// NB: Since wxWindows in Mac HAVE to have parents we need this to be +// a top level window... +//----------------------------------------------------------------------------- + +class wxTaskBarIconWindow : public wxTopLevelWindow +{ +public: + wxTaskBarIconWindow(wxTaskBarIconImpl *impl) + : wxTopLevelWindow(NULL, wxID_ANY, wxEmptyString), m_impl(impl) + { + Connect( + -1, wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(wxTaskBarIconWindow::OnMenuEvent) ); + } + + void OnMenuEvent(wxCommandEvent& event) + { + m_impl->m_parent->ProcessEvent(event); + } + +private: + wxTaskBarIconImpl *m_impl; +}; + +class wxDockTaskBarIcon : public wxTaskBarIconImpl +{ +public: + wxDockTaskBarIcon(wxTaskBarIcon* parent); + virtual ~wxDockTaskBarIcon(); + + virtual bool IsIconInstalled() const; + virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip); + virtual bool RemoveIcon(); + virtual bool PopupMenu(wxMenu *menu); + + wxMenu* DoCreatePopupMenu(); + + EventHandlerRef m_eventHandlerRef; + EventHandlerUPP m_eventupp; + wxWindow *m_eventWindow; + wxMenu *m_pMenu; + MenuRef m_theLastMenu; + bool m_iconAdded; +}; + +// Forward declarations for utility functions for dock implementation +pascal OSStatus wxDockEventHandler( + EventHandlerCallRef inHandlerCallRef, + EventRef inEvent, void* pData ); +wxMenu * wxDeepCopyMenu( wxMenu *menu ); + + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxTaskBarIconImpl +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +wxTaskBarIconImpl::wxTaskBarIconImpl(wxTaskBarIcon* parent) + : m_parent(parent), m_menuEventWindow(new wxTaskBarIconWindow(this)) +{ +} + +wxTaskBarIconImpl::~wxTaskBarIconImpl() +{ + delete m_menuEventWindow; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxDockTaskBarIcon +// +// OS X Dock implementation of wxTaskBarIcon using Carbon +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//----------------------------------------------------------------------------- +// wxDockEventHandler +// +// This is the global Mac/Carbon event handler for the dock. +// We need this for two reasons: +// 1) To handle wxTaskBarIcon menu events (see below for why) +// 2) To handle events from the dock when it requests a menu +//----------------------------------------------------------------------------- +pascal OSStatus +wxDockEventHandler(EventHandlerCallRef WXUNUSED(inHandlerCallRef), + EventRef inEvent, + void *pData) +{ + // Get the parameters we want from the event + wxDockTaskBarIcon* pTB = (wxDockTaskBarIcon*) pData; + const UInt32 eventClass = GetEventClass(inEvent); + const UInt32 eventKind = GetEventKind(inEvent); + + OSStatus err = eventNotHandledErr; + + // Handle wxTaskBar menu events (note that this is a global event handler + // so it will actually get called by all commands/menus) + if ((eventClass == kEventClassCommand) && (eventKind == kEventCommandProcess || eventKind == kEventCommandUpdateStatus )) + { + // if we have no taskbar menu quickly pass it back to wxApp + if (pTB->m_pMenu != NULL) + { + // This is the real reason why we need this. Normally menus + // get handled in wxMacAppEventHandler + // However, in the case of a taskbar menu call + // command.menu.menuRef IS NULL! + // Which causes the wxApp handler just to skip it. + + // get the HICommand from the event + HICommand command; + if (GetEventParameter(inEvent, kEventParamDirectObject, + typeHICommand, NULL,sizeof(HICommand), NULL, &command ) == noErr) + { + // Obtain the REAL menuRef and the menuItemIndex in the real menuRef + // + // NOTE: menuRef is generally used here for submenus, as + // GetMenuItemRefCon could give an incorrect wxMenuItem if we pass + // just the top level wxTaskBar menu + MenuItemIndex menuItemIndex; + MenuRef menuRef; + MenuRef taskbarMenuRef = MAC_WXHMENU(pTB->m_pMenu->GetHMenu()); + + // the next command is only successful if it was a command from the taskbar menu + // otherwise we pass it on + if (GetIndMenuItemWithCommandID(taskbarMenuRef,command.commandID, + 1, &menuRef, &menuItemIndex ) == noErr) + { + wxMenu* itemMenu = wxFindMenuFromMacMenu( menuRef ) ; + int id = wxMacCommandToId( command.commandID ) ; + wxMenuItem *item = NULL; + + if (id != 0) // get the wxMenuItem reference from the MenuRef + GetMenuItemRefCon( menuRef, menuItemIndex, (URefCon*) &item ); + + if (item && itemMenu ) + { + if ( eventKind == kEventCommandProcess ) + err = itemMenu->MacHandleCommandProcess( item, id ); + else if ( eventKind == kEventCommandUpdateStatus ) + err = itemMenu->MacHandleCommandUpdateStatus( item, id ); + } + } + } + } //end if noErr on getting HICommand from event + } + else if ((eventClass == kEventClassApplication) && (eventKind == kEventAppGetDockTileMenu )) + { + // process the right click events + // NB: This may result in double or even triple-creation of the menus + // We need to do this for 2.4 compat, however + wxTaskBarIconEvent downevt(wxEVT_TASKBAR_RIGHT_DOWN, NULL); + pTB->m_parent->ProcessEvent(downevt); + + wxTaskBarIconEvent upevt(wxEVT_TASKBAR_RIGHT_UP, NULL); + pTB->m_parent->ProcessEvent(upevt); + + // create popup menu + wxMenu* menu = pTB->DoCreatePopupMenu(); + + if (menu != NULL) + { + // note to self - a MenuRef *is* a MenuHandle + MenuRef hMenu = MAC_WXHMENU(menu->GetHMenu()); + + // When SetEventParameter is called it will decrement + // the reference count of the menu - we need to make + // sure it stays around in the wxMenu class here + CFRetain(hMenu); + + // set the actual dock menu + err = SetEventParameter( + inEvent, kEventParamMenuRef, + typeMenuRef, sizeof(MenuRef), &hMenu ); + verify_noerr( err ); + } + } + + return err; +} + +//----------------------------------------------------------------------------- +// wxDeepCopyMenu +// +// Performs a top-to-bottom copy of the input menu and all of its +// submenus. +// +// This is mostly needed for 2.4 compatability. However wxPython and others +// still use this way of setting the taskbarmenu. +//----------------------------------------------------------------------------- +wxMenu * wxDeepCopyMenu( wxMenu *menu ) +{ + if (menu == NULL) + return NULL; + + // NB: Here we have to perform a deep copy of the menu, + // copying each and every menu item from menu to m_pMenu. + // Other implementations use wxWindow::PopupMenu here, + // which idle execution until the user selects something, + // but since the Mac handles this internally, we can't - + // and have no way at all to idle it while the dock menu + // is being shown before menu goes out of scope (it may + // not be on the heap, and may expire right after this function + // is done - we need it to last until the carbon event is triggered - + // that's when the user right clicks). + // + // Also, since there is no equal (assignment) operator + // on either wxMenu or wxMenuItem, we have to do all the + // dirty work ourselves. + + // perform a deep copy of the menu + wxMenuItemList& theList = menu->GetMenuItems(); + wxMenuItemList::compatibility_iterator theNode = theList.GetFirst(); + + // create the main menu + wxMenu *m_pMenu = new wxMenu(menu->GetTitle()); + + while (theNode != NULL) + { + wxMenuItem* theItem = theNode->GetData(); + m_pMenu->Append( + new wxMenuItem( + m_pMenu, // parent menu + theItem->GetId(), // id + theItem->GetItemLabel(), // text label + theItem->GetHelp(), // status bar help string + theItem->GetKind(), // menu flags - checkable, separator, etc. + wxDeepCopyMenu(theItem->GetSubMenu()) )); // submenu + + theNode = theNode->GetNext(); + } + + return m_pMenu; +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon ctor +// +// Initializes the dock implementation of wxTaskBarIcon. +// +// Here we create some Mac-specific event handlers and UPPs. +//----------------------------------------------------------------------------- +wxDockTaskBarIcon::wxDockTaskBarIcon(wxTaskBarIcon* parent) + : wxTaskBarIconImpl(parent), + m_eventHandlerRef(NULL), m_pMenu(NULL), + m_theLastMenu(GetApplicationDockTileMenu()), m_iconAdded(false) +{ + // register the events that will return the dock menu + EventTypeSpec tbEventList[] = + { + { kEventClassCommand, kEventProcessCommand }, + { kEventClassCommand, kEventCommandUpdateStatus }, + { kEventClassApplication, kEventAppGetDockTileMenu } + }; + + m_eventupp = NewEventHandlerUPP(wxDockEventHandler); + wxASSERT(m_eventupp != NULL); + + OSStatus err = InstallApplicationEventHandler( + m_eventupp, + GetEventTypeCount(tbEventList), tbEventList, + this, &m_eventHandlerRef); + verify_noerr( err ); +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon Destructor +// +// Cleans up mac events and restores the old icon to the dock +//----------------------------------------------------------------------------- +wxDockTaskBarIcon::~wxDockTaskBarIcon() +{ + // clean up event handler and event UPP + RemoveEventHandler(m_eventHandlerRef); + DisposeEventHandlerUPP(m_eventupp); + + // restore old icon and menu to the dock + RemoveIcon(); +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::DoCreatePopupMenu +// +// Helper function that handles a request from the dock event handler +// to get the menu for the dock +//----------------------------------------------------------------------------- +wxMenu * wxDockTaskBarIcon::DoCreatePopupMenu() +{ + // get the menu from the parent + wxMenu* theNewMenu = CreatePopupMenu(); + + if (theNewMenu) + { + if (m_pMenu) + delete m_pMenu; + m_pMenu = theNewMenu; + m_pMenu->SetInvokingWindow(m_menuEventWindow); + } + + // the return here can be one of three things + // (in order of priority): + // 1) User passed a menu from CreatePopupMenu override + // 2) menu sent to and copied from PopupMenu + // 3) If neither (1) or (2), then NULL + // + return m_pMenu; +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::IsIconInstalled +// +// Returns whether or not the dock is not using the default image +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::IsIconInstalled() const +{ + return m_iconAdded; +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::SetIcon +// +// Sets the icon for the dock CGImage functions and SetApplicationDockTileImage +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& WXUNUSED(tooltip)) +{ + // convert the wxIcon into a wxBitmap so we can perform some + // wxBitmap operations with it + wxBitmap bmp( icon ); + wxASSERT( bmp.Ok() ); + + // get the CGImageRef for the wxBitmap: + // OSX builds only, but then the dock only exists in OSX + CGImageRef pImage = (CGImageRef) bmp.CreateCGImage(); + wxASSERT( pImage != NULL ); + + // actually set the dock image + OSStatus err = SetApplicationDockTileImage( pImage ); + verify_noerr( err ); + + // free the CGImage, now that it's referenced by the dock + if (pImage != NULL) + CGImageRelease( pImage ); + + bool success = (err == noErr); + m_iconAdded = success; + + return success; +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::RemoveIcon +// +// Restores the old image for the dock via RestoreApplicationDockTileImage +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::RemoveIcon() +{ + if (m_pMenu) + { + delete m_pMenu; + m_pMenu = NULL; + } + + // restore old icon to the dock + OSStatus err = RestoreApplicationDockTileImage(); + verify_noerr( err ); + + // restore the old menu to the dock + SetApplicationDockTileMenu( m_theLastMenu ); + + bool success = (err == noErr); + m_iconAdded = !success; + + return success; +} + +//----------------------------------------------------------------------------- +// wxDockTaskBarIcon::PopupMenu +// +// 2.4 and wxPython method that "pops of the menu in the taskbar". +// +// In reality because of the way the dock menu works in carbon +// we just save the menu, and if the user didn't override CreatePopupMenu +// return the menu passed here, thus sort of getting the same effect. +//----------------------------------------------------------------------------- +bool wxDockTaskBarIcon::PopupMenu(wxMenu *menu) +{ + wxASSERT(menu != NULL); + + if (m_pMenu) + delete m_pMenu; + + // start copy of menu + m_pMenu = wxDeepCopyMenu(menu); + + // finish up + m_pMenu->SetInvokingWindow(m_menuEventWindow); + + return true; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxTaskBarIcon +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler) + +//----------------------------------------------------------------------------- +// wxTaskBarIcon Constructor +// +// Creates the backend +// +// Note that we only support DOCK currently as others require cocoa and +// also some require hacks and other such things. (MenuExtras are +// actually seperate programs that also require a special undocumented id +// hack and other such fun stuff). +//----------------------------------------------------------------------------- +wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType nType) +{ + wxASSERT_MSG( + nType == DOCK, + wxT("Only the DOCK implementation of wxTaskBarIcon on Mac-Carbon is currently supported!") ); + + m_impl = new wxDockTaskBarIcon(this); +} + +//----------------------------------------------------------------------------- +// wxTaskBarIcon Destructor +// +// Destroys the backend +//----------------------------------------------------------------------------- +wxTaskBarIcon::~wxTaskBarIcon() +{ + delete m_impl; +} + +//----------------------------------------------------------------------------- +// wxTaskBarIcon::SetIcon +// wxTaskBarIcon::RemoveIcon +// wxTaskBarIcon::PopupMenu +// +// Just calls the backend version of the said function. +//----------------------------------------------------------------------------- +bool wxTaskBarIcon::IsIconInstalled() const +{ return m_impl->IsIconInstalled(); } + +bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip) +{ return m_impl->SetIcon(icon, tooltip); } + +bool wxTaskBarIcon::RemoveIcon() +{ return m_impl->RemoveIcon(); } + +bool wxTaskBarIcon::PopupMenu(wxMenu *menu) +{ return m_impl->PopupMenu(menu); } + +#endif // wxUSE_TASKBARICON diff --git a/src/osx/carbon/textctrl.cpp b/src/osx/carbon/textctrl.cpp new file mode 100644 index 0000000000..b162a58860 --- /dev/null +++ b/src/osx/carbon/textctrl.cpp @@ -0,0 +1,3129 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/textctrl.cpp +// Purpose: wxTextCtrl +// Author: Stefan Csomor +// Modified by: Ryan Norton (MLTE GetLineLength and GetLineText) +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_TEXTCTRL + +#include "wx/textctrl.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/dc.h" + #include "wx/button.h" + #include "wx/menu.h" + #include "wx/settings.h" + #include "wx/msgdlg.h" + #include "wx/toplevel.h" +#endif + +#ifdef __DARWIN__ + #include + #include +#else + #include +#endif + +#if wxUSE_STD_IOSTREAM + #if wxUSE_IOSTREAMH + #include + #else + #include + #endif +#endif + +#include "wx/filefn.h" +#include "wx/sysopt.h" +#include "wx/thread.h" + +#include "wx/mac/uma.h" +#include "wx/mac/carbon/private/mactext.h" + +class wxMacFunctor +{ +public : + wxMacFunctor() {} + virtual ~wxMacFunctor() {} + + virtual void* operator()() = 0 ; + + static void* CallBackProc( void *param ) + { + wxMacFunctor* f = (wxMacFunctor*) param ; + void *result = (*f)() ; + return result ; + } +} ; + +template + +class wxMacObjectFunctor1 : public wxMacFunctor +{ + typedef void (classtype::*function)( param1type p1 ) ; + typedef void (classtype::*ref_function)( const param1type& p1 ) ; +public : + wxMacObjectFunctor1( classtype *obj , function f , param1type p1 ) : + wxMacFunctor() + { + m_object = obj ; + m_function = f ; + m_param1 = p1 ; + } + + wxMacObjectFunctor1( classtype *obj , ref_function f , param1type p1 ) : + wxMacFunctor() + { + m_object = obj ; + m_refFunction = f ; + m_param1 = p1 ; + } + + virtual ~wxMacObjectFunctor1() {} + + virtual void* operator()() + { + (m_object->*m_function)( m_param1 ) ; + return NULL ; + } + +private : + classtype* m_object ; + param1type m_param1 ; + union + { + function m_function ; + ref_function m_refFunction ; + } ; +} ; + +template +void* wxMacMPRemoteCall( classtype *object , void (classtype::*function)( param1type p1 ) , param1type p1 ) +{ + wxMacObjectFunctor1 params(object, function, p1) ; + void *result = + MPRemoteCall( wxMacFunctor::CallBackProc , ¶ms , kMPOwningProcessRemoteContext ) ; + return result ; +} + +template +void* wxMacMPRemoteCall( classtype *object , void (classtype::*function)( const param1type& p1 ) , param1type p1 ) +{ + wxMacObjectFunctor1 params(object, function, p1) ; + void *result = + MPRemoteCall( wxMacFunctor::CallBackProc , ¶ms , kMPOwningProcessRemoteContext ) ; + return result ; +} + +template +void* wxMacMPRemoteGUICall( classtype *object , void (classtype::*function)( param1type p1 ) , param1type p1 ) +{ + wxMutexGuiLeave() ; + void *result = wxMacMPRemoteCall( object , function , p1 ) ; + wxMutexGuiEnter() ; + return result ; +} + +template +void* wxMacMPRemoteGUICall( classtype *object , void (classtype::*function)( const param1type& p1 ) , param1type p1 ) +{ + wxMutexGuiLeave() ; + void *result = wxMacMPRemoteCall( object , function , p1 ) ; + wxMutexGuiEnter() ; + return result ; +} + +class WXDLLEXPORT wxMacPortSaver +{ + DECLARE_NO_COPY_CLASS(wxMacPortSaver) + +public: + wxMacPortSaver( GrafPtr port ); + ~wxMacPortSaver(); +private : + GrafPtr m_port; +}; + + +/* + Clips to the visible region of a control within the current port + */ + +class WXDLLEXPORT wxMacWindowClipper : public wxMacPortSaver +{ + DECLARE_NO_COPY_CLASS(wxMacWindowClipper) + +public: + wxMacWindowClipper( const wxWindow* win ); + ~wxMacWindowClipper(); +private: + GrafPtr m_newPort; + RgnHandle m_formerClip; + RgnHandle m_newClip; +}; + +wxMacPortSaver::wxMacPortSaver( GrafPtr port ) +{ + ::GetPort( &m_port ); + ::SetPort( port ); +} + +wxMacPortSaver::~wxMacPortSaver() +{ + ::SetPort( m_port ); +} + +wxMacWindowClipper::wxMacWindowClipper( const wxWindow* win ) : +wxMacPortSaver( (GrafPtr) GetWindowPort( (WindowRef) win->MacGetTopLevelWindowRef() ) ) +{ + m_newPort = (GrafPtr) GetWindowPort( (WindowRef) win->MacGetTopLevelWindowRef() ) ; + m_formerClip = NewRgn() ; + m_newClip = NewRgn() ; + GetClip( m_formerClip ) ; + + if ( win ) + { + // guard against half constructed objects, this just leads to a empty clip + if ( win->GetPeer() ) + { + int x = 0 , y = 0; + win->MacWindowToRootWindow( &x, &y ) ; + + // get area including focus rect + HIShapeGetAsQDRgn( ((wxWindow*)win)->MacGetVisibleRegion(true).GetWXHRGN() , m_newClip ); + if ( !EmptyRgn( m_newClip ) ) + OffsetRgn( m_newClip , x , y ) ; + } + + SetClip( m_newClip ) ; + } +} + +wxMacWindowClipper::~wxMacWindowClipper() +{ + SetPort( m_newPort ) ; + SetClip( m_formerClip ) ; + DisposeRgn( m_newClip ) ; + DisposeRgn( m_formerClip ) ; +} + +// common parts for implementations based on MLTE + +class wxMacMLTEControl : public wxMacTextControl +{ +public : + wxMacMLTEControl( wxTextCtrl *peer ) ; + + virtual wxString GetStringValue() const ; + virtual void SetStringValue( const wxString &str ) ; + + static TXNFrameOptions FrameOptionsFromWXStyle( long wxStyle ) ; + + void AdjustCreationAttributes( const wxColour& background, bool visible ) ; + + virtual void SetFont( const wxFont & font, const wxColour& foreground, long windowStyle ) ; + virtual void SetBackgroundColour(const wxColour& col ); + virtual void SetStyle( long start, long end, const wxTextAttr& style ) ; + virtual void Copy() ; + virtual void Cut() ; + virtual void Paste() ; + virtual bool CanPaste() const ; + virtual void SetEditable( bool editable ) ; + virtual wxTextPos GetLastPosition() const ; + virtual void Replace( long from, long to, const wxString &str ) ; + virtual void Remove( long from, long to ) ; + virtual void GetSelection( long* from, long* to ) const ; + virtual void SetSelection( long from, long to ) ; + + virtual void WriteText( const wxString& str ) ; + + virtual bool HasOwnContextMenu() const + { + TXNCommandEventSupportOptions options ; + TXNGetCommandEventSupport( m_txn , & options ) ; + return options & kTXNSupportEditCommandProcessing ; + } + + virtual void CheckSpelling(bool check) + { + TXNSetSpellCheckAsYouType( m_txn, (Boolean) check ); + } + virtual void Clear() ; + + virtual bool CanUndo() const ; + virtual void Undo() ; + virtual bool CanRedo() const; + virtual void Redo() ; + virtual int GetNumberOfLines() const ; + virtual long XYToPosition(long x, long y) const ; + virtual bool PositionToXY(long pos, long *x, long *y) const ; + virtual void ShowPosition( long pos ) ; + virtual int GetLineLength(long lineNo) const ; + virtual wxString GetLineText(long lineNo) const ; + + void SetTXNData( const wxString& st , TXNOffset start , TXNOffset end ) ; + TXNObject GetTXNObject() { return m_txn ; } + +protected : + void TXNSetAttribute( const wxTextAttr& style , long from , long to ) ; + + TXNObject m_txn ; +} ; + +// implementation available under OSX + +class wxMacMLTEHIViewControl : public wxMacMLTEControl +{ +public : + wxMacMLTEHIViewControl( wxTextCtrl *wxPeer, + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) ; + virtual ~wxMacMLTEHIViewControl() ; + + virtual OSStatus SetFocus( ControlFocusPart focusPart ) ; + virtual bool HasFocus() const ; + virtual void SetBackgroundColour(const wxColour& col ) ; + +protected : + HIViewRef m_scrollView ; + HIViewRef m_textView ; +}; + +// 'classic' MLTE implementation + +class wxMacMLTEClassicControl : public wxMacMLTEControl +{ +public : + wxMacMLTEClassicControl( wxTextCtrl *wxPeer, + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) ; + virtual ~wxMacMLTEClassicControl() ; + + virtual void VisibilityChanged(bool shown) ; + virtual void SuperChangedPosition() ; + + virtual void MacControlUserPaneDrawProc(wxInt16 part) ; + virtual wxInt16 MacControlUserPaneHitTestProc(wxInt16 x, wxInt16 y) ; + virtual wxInt16 MacControlUserPaneTrackingProc(wxInt16 x, wxInt16 y, void* actionProc) ; + virtual void MacControlUserPaneIdleProc() ; + virtual wxInt16 MacControlUserPaneKeyDownProc(wxInt16 keyCode, wxInt16 charCode, wxInt16 modifiers) ; + virtual void MacControlUserPaneActivateProc(bool activating) ; + virtual wxInt16 MacControlUserPaneFocusProc(wxInt16 action) ; + virtual void MacControlUserPaneBackgroundProc(void* info) ; + + virtual bool SetupCursor( const wxPoint& WXUNUSED(pt) ) + { + MacControlUserPaneIdleProc(); + return true; + } + + virtual void SetRect( Rect *r ) ; + +protected : + OSStatus DoCreate(); + + void MacUpdatePosition() ; + void MacActivatePaneText(bool setActive) ; + void MacFocusPaneText(bool setFocus) ; + void MacSetObjectVisibility(bool vis) ; + +private : + TXNFrameID m_txnFrameID ; + GrafPtr m_txnPort ; + WindowRef m_txnWindow ; + // bounds of the control as we last did set the txn frames + Rect m_txnControlBounds ; + Rect m_txnVisBounds ; + + static pascal void TXNScrollActionProc( ControlRef controlRef , ControlPartCode partCode ) ; + static pascal void TXNScrollInfoProc( + SInt32 iValue, SInt32 iMaximumValue, + TXNScrollBarOrientation iScrollBarOrientation, SInt32 iRefCon ) ; + + ControlRef m_sbHorizontal ; + SInt32 m_lastHorizontalValue ; + ControlRef m_sbVertical ; + SInt32 m_lastVerticalValue ; +}; + + +IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase) + +BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase) + EVT_DROP_FILES(wxTextCtrl::OnDropFiles) + EVT_CHAR(wxTextCtrl::OnChar) + EVT_MENU(wxID_CUT, wxTextCtrl::OnCut) + EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy) + EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste) + EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo) + EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo) + EVT_MENU(wxID_CLEAR, wxTextCtrl::OnDelete) + EVT_MENU(wxID_SELECTALL, wxTextCtrl::OnSelectAll) + + EVT_CONTEXT_MENU(wxTextCtrl::OnContextMenu) + + EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut) + EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy) + EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste) + EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo) + EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo) + EVT_UPDATE_UI(wxID_CLEAR, wxTextCtrl::OnUpdateDelete) + EVT_UPDATE_UI(wxID_SELECTALL, wxTextCtrl::OnUpdateSelectAll) +END_EVENT_TABLE() + + +void wxTextCtrl::Init() +{ + m_editable = true ; + m_dirty = false; + + m_maxLength = 0; + m_privateContextMenu = NULL; + m_triggerOnSetValue = true ; +} + +wxTextCtrl::~wxTextCtrl() +{ + delete m_privateContextMenu; +} + +bool wxTextCtrl::Create( wxWindow *parent, + wxWindowID id, + const wxString& str, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name ) +{ + m_macIsUserPane = false ; + m_editable = true ; + + if ( ! (style & wxNO_BORDER) ) + style = (style & ~wxBORDER_MASK) | wxSUNKEN_BORDER ; + + if ( !wxTextCtrlBase::Create( parent, id, pos, size, style & ~(wxHSCROLL | wxVSCROLL), validator, name ) ) + return false; + + if ( m_windowStyle & wxTE_MULTILINE ) + { + // always turn on this style for multi-line controls + m_windowStyle |= wxTE_PROCESS_ENTER; + style |= wxTE_PROCESS_ENTER ; + } + + CreatePeer( str, pos, size, style ); + + MacPostControlCreate(pos, size) ; + + // only now the embedding is correct and we can do a positioning update + + MacSuperChangedPosition() ; + + if ( m_windowStyle & wxTE_READONLY) + SetEditable( false ) ; + + SetCursor( wxCursor( wxCURSOR_IBEAM ) ) ; + + return true; +} + +void wxTextCtrl::CreatePeer( + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) +{ + bool forceMLTE = false ; + +#if wxUSE_SYSTEM_OPTIONS + if (wxSystemOptions::HasOption( wxMAC_TEXTCONTROL_USE_MLTE ) && (wxSystemOptions::GetOptionInt( wxMAC_TEXTCONTROL_USE_MLTE ) == 1)) + { + forceMLTE = true ; + } +#endif + + if ( UMAGetSystemVersion() >= 0x1050 ) + forceMLTE = false; + + if ( !forceMLTE ) + { + if ( m_windowStyle & wxTE_MULTILINE || ( UMAGetSystemVersion() >= 0x1050 ) ) + m_peer = new wxMacMLTEHIViewControl( this , str , pos , size , style ) ; + } + + if ( !m_peer ) + { + if ( !(m_windowStyle & wxTE_MULTILINE) && !forceMLTE ) + { + m_peer = new wxMacUnicodeTextControl( this , str , pos , size , style ) ; + } + } + + // the horizontal single line scrolling bug that made us keep the classic implementation + // is fixed in 10.5 +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + if ( !m_peer ) + m_peer = new wxMacMLTEClassicControl( this , str , pos , size , style ) ; +#endif +} + +void wxTextCtrl::MacSuperChangedPosition() +{ + wxWindow::MacSuperChangedPosition() ; + GetPeer()->SuperChangedPosition() ; +} + +void wxTextCtrl::MacVisibilityChanged() +{ + GetPeer()->VisibilityChanged( GetPeer()->IsVisible() ); +} + +void wxTextCtrl::MacCheckSpelling(bool check) +{ + GetPeer()->CheckSpelling(check); +} + +wxString wxTextCtrl::GetValue() const +{ + return GetPeer()->GetStringValue() ; +} + +void wxTextCtrl::GetSelection(long* from, long* to) const +{ + GetPeer()->GetSelection( from , to ) ; +} + +void wxTextCtrl::DoSetValue(const wxString& str, int flags) +{ + // optimize redraws + if ( GetValue() == str ) + return; + + GetPeer()->SetStringValue( str ) ; + + if ( (flags & SetValue_SendEvent) && m_triggerOnSetValue ) + { + SendTextUpdatedEvent(); + } +} + +void wxTextCtrl::SetMaxLength(unsigned long len) +{ + m_maxLength = len ; +} + +bool wxTextCtrl::SetFont( const wxFont& font ) +{ + if ( !wxTextCtrlBase::SetFont( font ) ) + return false ; + + GetPeer()->SetFont( font , GetForegroundColour() , GetWindowStyle() ) ; + + return true ; +} + +bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) +{ + GetPeer()->SetStyle( start , end , style ) ; + + return true ; +} + +bool wxTextCtrl::SetDefaultStyle(const wxTextAttr& style) +{ + wxTextCtrlBase::SetDefaultStyle( style ) ; + SetStyle( kTXNUseCurrentSelection , kTXNUseCurrentSelection , GetDefaultStyle() ) ; + + return true ; +} + +// Clipboard operations + +void wxTextCtrl::Copy() +{ + if (CanCopy()) + GetPeer()->Copy() ; +} + +void wxTextCtrl::Cut() +{ + if (CanCut()) + { + GetPeer()->Cut() ; + + wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, m_windowId ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + } +} + +void wxTextCtrl::Paste() +{ + if (CanPaste()) + { + GetPeer()->Paste() ; + + // TODO: eventually we should add setting the default style again + + wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, m_windowId ); + event.SetEventObject( this ); + HandleWindowEvent( event ); + } +} + +bool wxTextCtrl::CanCopy() const +{ + // Can copy if there's a selection + long from, to; + GetSelection( &from, &to ); + + return (from != to); +} + +bool wxTextCtrl::CanCut() const +{ + if ( !IsEditable() ) + return false; + + // Can cut if there's a selection + long from, to; + GetSelection( &from, &to ); + + return (from != to); +} + +bool wxTextCtrl::CanPaste() const +{ + if (!IsEditable()) + return false; + + return GetPeer()->CanPaste() ; +} + +void wxTextCtrl::SetEditable(bool editable) +{ + if ( editable != m_editable ) + { + m_editable = editable ; + GetPeer()->SetEditable( editable ) ; + } +} + +void wxTextCtrl::SetInsertionPoint(long pos) +{ + SetSelection( pos , pos ) ; +} + +void wxTextCtrl::SetInsertionPointEnd() +{ + wxTextPos pos = GetLastPosition(); + SetInsertionPoint( pos ); +} + +long wxTextCtrl::GetInsertionPoint() const +{ + long begin, end ; + GetSelection( &begin , &end ) ; + + return begin ; +} + +wxTextPos wxTextCtrl::GetLastPosition() const +{ + return GetPeer()->GetLastPosition() ; +} + +void wxTextCtrl::Replace(long from, long to, const wxString& str) +{ + GetPeer()->Replace( from , to , str ) ; +} + +void wxTextCtrl::Remove(long from, long to) +{ + GetPeer()->Remove( from , to ) ; +} + +void wxTextCtrl::SetSelection(long from, long to) +{ + GetPeer()->SetSelection( from , to ) ; +} + +void wxTextCtrl::WriteText(const wxString& str) +{ + // TODO: this MPRemoting will be moved into a remoting peer proxy for any command + if ( !wxIsMainThread() ) + { + // unfortunately CW 8 is not able to correctly deduce the template types, + // so we have to instantiate explicitly + wxMacMPRemoteGUICall( this , &wxTextCtrl::WriteText , str ) ; + + return ; + } + + GetPeer()->WriteText( str ) ; +} + +void wxTextCtrl::AppendText(const wxString& text) +{ + SetInsertionPointEnd(); + WriteText( text ); +} + +void wxTextCtrl::Clear() +{ + GetPeer()->Clear() ; +} + +bool wxTextCtrl::IsModified() const +{ + return m_dirty; +} + +bool wxTextCtrl::IsEditable() const +{ + return IsEnabled() && m_editable ; +} + +bool wxTextCtrl::AcceptsFocus() const +{ + // we don't want focus if we can't be edited + return /*IsEditable() && */ wxControl::AcceptsFocus(); +} + +wxSize wxTextCtrl::DoGetBestSize() const +{ + int wText, hText; + + // these are the numbers from the HIG: + // we reduce them by the borders first + wText = 100 ; + + switch ( m_windowVariant ) + { + case wxWINDOW_VARIANT_NORMAL : + hText = 22 - 6 ; + break ; + + case wxWINDOW_VARIANT_SMALL : + hText = 19 - 6 ; + break ; + + case wxWINDOW_VARIANT_MINI : + hText = 15 - 6 ; + break ; + + default : + hText = 22 - 6; + break ; + } + + // as the above numbers have some free space around the text + // we get 5 lines like this anyway + if ( m_windowStyle & wxTE_MULTILINE ) + hText *= 5 ; + + if ( !HasFlag(wxNO_BORDER) ) + hText += 6 ; + + return wxSize(wText, hText); +} + +// ---------------------------------------------------------------------------- +// Undo/redo +// ---------------------------------------------------------------------------- + +void wxTextCtrl::Undo() +{ + if (CanUndo()) + GetPeer()->Undo() ; +} + +void wxTextCtrl::Redo() +{ + if (CanRedo()) + GetPeer()->Redo() ; +} + +bool wxTextCtrl::CanUndo() const +{ + if ( !IsEditable() ) + return false ; + + return GetPeer()->CanUndo() ; +} + +bool wxTextCtrl::CanRedo() const +{ + if ( !IsEditable() ) + return false ; + + return GetPeer()->CanRedo() ; +} + +void wxTextCtrl::MarkDirty() +{ + m_dirty = true; +} + +void wxTextCtrl::DiscardEdits() +{ + m_dirty = false; +} + +int wxTextCtrl::GetNumberOfLines() const +{ + return GetPeer()->GetNumberOfLines() ; +} + +long wxTextCtrl::XYToPosition(long x, long y) const +{ + return GetPeer()->XYToPosition( x , y ) ; +} + +bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const +{ + return GetPeer()->PositionToXY( pos , x , y ) ; +} + +void wxTextCtrl::ShowPosition(long pos) +{ + return GetPeer()->ShowPosition(pos) ; +} + +int wxTextCtrl::GetLineLength(long lineNo) const +{ + return GetPeer()->GetLineLength(lineNo) ; +} + +wxString wxTextCtrl::GetLineText(long lineNo) const +{ + return GetPeer()->GetLineText(lineNo) ; +} + +void wxTextCtrl::Command(wxCommandEvent & event) +{ + SetValue(event.GetString()); + ProcessCommand(event); +} + +void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event) +{ + // By default, load the first file into the text window. + if (event.GetNumberOfFiles() > 0) + LoadFile( event.GetFiles()[0] ); +} + +void wxTextCtrl::OnChar(wxKeyEvent& event) +{ + int key = event.GetKeyCode() ; + bool eat_key = false ; + long from, to; + + if ( key == 'a' && event.MetaDown() ) + { + SelectAll() ; + + return ; + } + + if ( key == 'c' && event.MetaDown() ) + { + if ( CanCopy() ) + Copy() ; + + return ; + } + + if ( !IsEditable() && key != WXK_LEFT && key != WXK_RIGHT && key != WXK_DOWN && key != WXK_UP && key != WXK_TAB && + !( key == WXK_RETURN && ( (m_windowStyle & wxTE_PROCESS_ENTER) || (m_windowStyle & wxTE_MULTILINE) ) ) +// && key != WXK_PAGEUP && key != WXK_PAGEDOWN && key != WXK_HOME && key != WXK_END + ) + { + // eat it + return ; + } + + // Check if we have reached the max # of chars (if it is set), but still + // allow navigation and deletion + GetSelection( &from, &to ); + if ( !IsMultiLine() && m_maxLength && GetValue().length() >= m_maxLength && + key != WXK_LEFT && key != WXK_RIGHT && key != WXK_TAB && key != WXK_UP && key != WXK_DOWN && + key != WXK_BACK && key != WXK_DELETE && !( key == WXK_RETURN && (m_windowStyle & wxTE_PROCESS_ENTER) ) && + from == to ) + { + // eat it, we don't want to add more than allowed # of characters + + // TODO: generate EVT_TEXT_MAXLEN() + return; + } + + // assume that any key not processed yet is going to modify the control + m_dirty = true; + + if ( key == 'v' && event.MetaDown() ) + { + if ( CanPaste() ) + Paste() ; + + return ; + } + + if ( key == 'x' && event.MetaDown() ) + { + if ( CanCut() ) + Cut() ; + + return ; + } + + switch ( key ) + { + case WXK_RETURN: + if (m_windowStyle & wxTE_PROCESS_ENTER) + { + wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId); + event.SetEventObject( this ); + event.SetString( GetValue() ); + if ( HandleWindowEvent(event) ) + return; + } + + if ( !(m_windowStyle & wxTE_MULTILINE) ) + { + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); + if ( tlw && tlw->GetDefaultItem() ) + { + wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton); + if ( def && def->IsEnabled() ) + { + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() ); + event.SetEventObject(def); + def->Command(event); + + return ; + } + } + + // this will make wxWidgets eat the ENTER key so that + // we actually prevent line wrapping in a single line text control + eat_key = true; + } + break; + + case WXK_TAB: + if ( !(m_windowStyle & wxTE_PROCESS_TAB)) + { + int flags = 0; + if (!event.ShiftDown()) + flags |= wxNavigationKeyEvent::IsForward ; + if (event.ControlDown()) + flags |= wxNavigationKeyEvent::WinChange ; + Navigate(flags); + + return; + } + else + { + // This is necessary (don't know why); + // otherwise the tab will not be inserted. + WriteText(wxT("\t")); + eat_key = true; + } + break; + + default: + break; + } + + if (!eat_key) + { + // perform keystroke handling + event.Skip(true) ; + } + + if ( ( key >= 0x20 && key < WXK_START ) || + ( key >= WXK_NUMPAD0 && key <= WXK_DIVIDE ) || + key == WXK_RETURN || + key == WXK_DELETE || + key == WXK_BACK) + { + wxCommandEvent event1(wxEVT_COMMAND_TEXT_UPDATED, m_windowId); + event1.SetEventObject( this ); + wxPostEvent( GetEventHandler(), event1 ); + } +} + +// ---------------------------------------------------------------------------- +// standard handlers for standard edit menu events +// ---------------------------------------------------------------------------- + +void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event)) +{ + Cut(); +} + +void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event)) +{ + Copy(); +} + +void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event)) +{ + Paste(); +} + +void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event)) +{ + Undo(); +} + +void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event)) +{ + Redo(); +} + +void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event)) +{ + long from, to; + + GetSelection( &from, &to ); + if (from != -1 && to != -1) + Remove( from, to ); +} + +void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event)) +{ + SetSelection(-1, -1); +} + +void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event) +{ + event.Enable( CanCut() ); +} + +void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event) +{ + event.Enable( CanCopy() ); +} + +void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event) +{ + event.Enable( CanPaste() ); +} + +void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event) +{ + event.Enable( CanUndo() ); +} + +void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event) +{ + event.Enable( CanRedo() ); +} + +void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event) +{ + long from, to; + + GetSelection( &from, &to ); + event.Enable( from != -1 && to != -1 && from != to && IsEditable() ) ; +} + +void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event) +{ + event.Enable(GetLastPosition() > 0); +} + +// CS: Context Menus only work with MLTE implementations or non-multiline HIViews at the moment + +void wxTextCtrl::OnContextMenu(wxContextMenuEvent& event) +{ + if ( GetPeer()->HasOwnContextMenu() ) + { + event.Skip() ; + return ; + } + + if (m_privateContextMenu == NULL) + { + m_privateContextMenu = new wxMenu; + m_privateContextMenu->Append(wxID_UNDO, _("&Undo")); + m_privateContextMenu->Append(wxID_REDO, _("&Redo")); + m_privateContextMenu->AppendSeparator(); + m_privateContextMenu->Append(wxID_CUT, _("Cu&t")); + m_privateContextMenu->Append(wxID_COPY, _("&Copy")); + m_privateContextMenu->Append(wxID_PASTE, _("&Paste")); + m_privateContextMenu->Append(wxID_CLEAR, _("&Delete")); + m_privateContextMenu->AppendSeparator(); + m_privateContextMenu->Append(wxID_SELECTALL, _("Select &All")); + } + + if (m_privateContextMenu != NULL) + PopupMenu(m_privateContextMenu); +} + +bool wxTextCtrl::MacSetupCursor( const wxPoint& pt ) +{ + if ( !GetPeer()->SetupCursor( pt ) ) + return wxWindow::MacSetupCursor( pt ) ; + else + return true ; +} + +// ---------------------------------------------------------------------------- +// implementation base class +// ---------------------------------------------------------------------------- + +wxMacTextControl::wxMacTextControl(wxTextCtrl* peer) : + wxMacControl( peer ) +{ +} + +wxMacTextControl::~wxMacTextControl() +{ +} + +void wxMacTextControl::SetStyle(long WXUNUSED(start), + long WXUNUSED(end), + const wxTextAttr& WXUNUSED(style)) +{ +} + +void wxMacTextControl::Copy() +{ +} + +void wxMacTextControl::Cut() +{ +} + +void wxMacTextControl::Paste() +{ +} + +bool wxMacTextControl::CanPaste() const +{ + return false ; +} + +void wxMacTextControl::SetEditable(bool WXUNUSED(editable)) +{ +} + +wxTextPos wxMacTextControl::GetLastPosition() const +{ + return GetStringValue().length() ; +} + +void wxMacTextControl::Replace( long from , long to , const wxString &val ) +{ + SetSelection( from , to ) ; + WriteText( val ) ; +} + +void wxMacTextControl::Remove( long from , long to ) +{ + SetSelection( from , to ) ; + WriteText( wxEmptyString) ; +} + +void wxMacTextControl::Clear() +{ + SetStringValue( wxEmptyString ) ; +} + +bool wxMacTextControl::CanUndo() const +{ + return false ; +} + +void wxMacTextControl::Undo() +{ +} + +bool wxMacTextControl::CanRedo() const +{ + return false ; +} + +void wxMacTextControl::Redo() +{ +} + +long wxMacTextControl::XYToPosition(long WXUNUSED(x), long WXUNUSED(y)) const +{ + return 0 ; +} + +bool wxMacTextControl::PositionToXY(long WXUNUSED(pos), + long *WXUNUSED(x), + long *WXUNUSED(y)) const +{ + return false ; +} + +void wxMacTextControl::ShowPosition( long WXUNUSED(pos) ) +{ +} + +int wxMacTextControl::GetNumberOfLines() const +{ + ItemCount lines = 0 ; + wxString content = GetStringValue() ; + lines = 1; + + for (size_t i = 0; i < content.length() ; i++) + { + if (content[i] == '\r') + lines++; + } + + return lines ; +} + +wxString wxMacTextControl::GetLineText(long lineNo) const +{ + // TODO: change this if possible to reflect real lines + wxString content = GetStringValue() ; + + // Find line first + int count = 0; + for (size_t i = 0; i < content.length() ; i++) + { + if (count == lineNo) + { + // Add chars in line then + wxString tmp; + + for (size_t j = i; j < content.length(); j++) + { + if (content[j] == '\n') + return tmp; + + tmp += content[j]; + } + + return tmp; + } + + if (content[i] == '\n') + count++; + } + + return wxEmptyString ; +} + +int wxMacTextControl::GetLineLength(long lineNo) const +{ + // TODO: change this if possible to reflect real lines + wxString content = GetStringValue() ; + + // Find line first + int count = 0; + for (size_t i = 0; i < content.length() ; i++) + { + if (count == lineNo) + { + // Count chars in line then + count = 0; + for (size_t j = i; j < content.length(); j++) + { + count++; + if (content[j] == '\n') + return count; + } + + return count; + } + + if (content[i] == '\n') + count++; + } + + return 0 ; +} + +void wxMacTextControl::SetFont( const wxFont & font , const wxColour& foreground , long windowStyle ) +{ + wxMacControl::SetFont(font, foreground, windowStyle ); +#ifndef __LP64__ + + // overrule the barrier in wxMacControl for supporting disabled controls, in order to support + // setting the color to eg red and back to black by controllers + + if ( foreground == *wxBLACK ) + { + ControlFontStyleRec fontStyle; + fontStyle.foreColor.red = fontStyle.foreColor.green = fontStyle.foreColor.blue = 0; + fontStyle.flags = kControlUseForeColorMask; + ::SetControlFontStyle( m_controlRef , &fontStyle ); + } +#endif +} + +// ---------------------------------------------------------------------------- +// standard unicode control implementation +// ---------------------------------------------------------------------------- + +// the current unicode textcontrol implementation has a bug : only if the control +// is currently having the focus, the selection can be retrieved by the corresponding +// data tag. So we have a mirroring using a member variable +// TODO : build event table using virtual member functions for wxMacControl + +static const EventTypeSpec unicodeTextControlEventList[] = +{ + { kEventClassControl , kEventControlSetFocusPart } , +} ; + +static pascal OSStatus wxMacUnicodeTextControlControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + wxMacUnicodeTextControl* focus = (wxMacUnicodeTextControl*) data ; + wxMacCarbonEvent cEvent( event ) ; + + switch ( GetEventKind( event ) ) + { + case kEventControlSetFocusPart : + { + ControlPartCode controlPart = cEvent.GetParameter(kEventParamControlPart , typeControlPartCode ); + if ( controlPart == kControlFocusNoPart ) + { + // about to loose focus -> store selection to field + focus->GetData( 0, kControlEditTextSelectionTag, &focus->m_selection ); + } + result = CallNextEventHandler(handler,event) ; + if ( controlPart != kControlFocusNoPart ) + { + // about to gain focus -> set selection from field + focus->SetData( 0, kControlEditTextSelectionTag, &focus->m_selection ); + } + break; + } + default: + break ; + } + + return result ; +} + +static pascal OSStatus wxMacUnicodeTextControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + + switch ( GetEventClass( event ) ) + { + case kEventClassControl : + result = wxMacUnicodeTextControlControlEventHandler( handler , event , data ) ; + break ; + + default : + break ; + } + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacUnicodeTextControlEventHandler ) + +wxMacUnicodeTextControl::wxMacUnicodeTextControl( wxTextCtrl *wxPeer ) : wxMacTextControl( wxPeer ) +{ +} + +wxMacUnicodeTextControl::wxMacUnicodeTextControl( wxTextCtrl *wxPeer, + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) + : wxMacTextControl( wxPeer ) +{ + Create( wxPeer, str, pos, size, style ); +} + +bool wxMacUnicodeTextControl::Create( wxTextCtrl *wxPeer, + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) +{ + m_font = wxPeer->GetFont() ; + m_windowStyle = style ; + m_selection.selStart = m_selection.selEnd = 0; + Rect bounds = wxMacGetBoundsForControl( wxPeer , pos , size ) ; + wxString st = str ; + wxMacConvertNewlines10To13( &st ) ; + wxCFStringRef cf(st , m_font.GetEncoding()) ; + CFStringRef cfr = cf ; + + m_valueTag = kControlEditTextCFStringTag ; + CreateControl( wxPeer, &bounds, cfr ); + + if ( !(m_windowStyle & wxTE_MULTILINE) ) + SetData( kControlEditTextPart , kControlEditTextSingleLineTag , true ) ; + + InstallControlEventHandler( m_controlRef , GetwxMacUnicodeTextControlEventHandlerUPP(), + GetEventTypeCount(unicodeTextControlEventList), unicodeTextControlEventList, this, + NULL); + + return true; +} + +wxMacUnicodeTextControl::~wxMacUnicodeTextControl() +{ +} + +void wxMacUnicodeTextControl::VisibilityChanged(bool shown) +{ + if ( !(m_windowStyle & wxTE_MULTILINE) && shown ) + { + // work around a refresh issue insofar as not always the entire content is shown, + // even if this would be possible + ControlEditTextSelectionRec sel ; + CFStringRef value = NULL ; + + verify_noerr( GetData( 0, kControlEditTextSelectionTag, &sel ) ); + verify_noerr( GetData( 0, m_valueTag, &value ) ); + verify_noerr( SetData( 0, m_valueTag, &value ) ); + verify_noerr( SetData( 0, kControlEditTextSelectionTag, &sel ) ); + + CFRelease( value ) ; + } +} + +wxString wxMacUnicodeTextControl::GetStringValue() const +{ + wxString result ; + CFStringRef value = GetData(0, m_valueTag) ; + if ( value ) + { + wxCFStringRef cf(value) ; + result = cf.AsString() ; + } + +#if '\n' == 10 + wxMacConvertNewlines13To10( &result ) ; +#else + wxMacConvertNewlines10To13( &result ) ; +#endif + + return result ; +} + +void wxMacUnicodeTextControl::SetStringValue( const wxString &str ) +{ + wxString st = str ; + wxMacConvertNewlines10To13( &st ) ; + wxCFStringRef cf( st , m_font.GetEncoding() ) ; + verify_noerr( SetData( 0, m_valueTag , cf ) ) ; +} + +void wxMacUnicodeTextControl::CreateControl( wxTextCtrl* peer, const Rect* bounds, CFStringRef cfr ) +{ + Boolean isPassword = ( m_windowStyle & wxTE_PASSWORD ) != 0 ; + if ( isPassword ) + { + m_valueTag = kControlEditTextPasswordCFStringTag ; + } + OSStatus err = CreateEditUnicodeTextControl( + MAC_WXHWND(peer->MacGetTopLevelWindowRef()), bounds , cfr , + isPassword , NULL , &m_controlRef ) ; + verify_noerr( err ); +} + +void wxMacUnicodeTextControl::Copy() +{ + SendHICommand( kHICommandCopy ) ; +} + +void wxMacUnicodeTextControl::Cut() +{ + SendHICommand( kHICommandCut ) ; +} + +void wxMacUnicodeTextControl::Paste() +{ + SendHICommand( kHICommandPaste ) ; +} + +bool wxMacUnicodeTextControl::CanPaste() const +{ + return true ; +} + +void wxMacUnicodeTextControl::SetEditable(bool WXUNUSED(editable)) +{ +#if 0 // leads to problem because text cannot be selected anymore + SetData( kControlEditTextPart , kControlEditTextLockedTag , (Boolean) !editable ) ; +#endif +} + +void wxMacUnicodeTextControl::GetSelection( long* from, long* to ) const +{ + ControlEditTextSelectionRec sel ; + if (HasFocus()) + verify_noerr( GetData( 0, kControlEditTextSelectionTag, &sel ) ) ; + else + sel = m_selection ; + + if ( from ) + *from = sel.selStart ; + if ( to ) + *to = sel.selEnd ; +} + +void wxMacUnicodeTextControl::SetSelection( long from , long to ) +{ + ControlEditTextSelectionRec sel ; + wxString result ; + int textLength = 0 ; + CFStringRef value = GetData(0, m_valueTag) ; + if ( value ) + { + wxCFStringRef cf(value) ; + textLength = cf.AsString().length() ; + } + + if ((from == -1) && (to == -1)) + { + from = 0 ; + to = textLength ; + } + else + { + from = wxMin(textLength,wxMax(from,0)) ; + if ( to == -1 ) + to = textLength; + else + to = wxMax(0,wxMin(textLength,to)) ; + } + + sel.selStart = from ; + sel.selEnd = to ; + if ( HasFocus() ) + SetData( 0, kControlEditTextSelectionTag, &sel ) ; + else + m_selection = sel; +} + +void wxMacUnicodeTextControl::WriteText( const wxString& str ) +{ + wxString st = str ; + wxMacConvertNewlines10To13( &st ) ; + + if ( HasFocus() ) + { + wxCFStringRef cf(st , m_font.GetEncoding() ) ; + CFStringRef value = cf ; + SetData( 0, kControlEditTextInsertCFStringRefTag, &value ); + } + else + { + wxString val = GetStringValue() ; + long start , end ; + GetSelection( &start , &end ) ; + val.Remove( start , end - start ) ; + val.insert( start , str ) ; + SetStringValue( val ) ; + SetSelection( start + str.length() , start + str.length() ) ; + } +} + +// ---------------------------------------------------------------------------- +// MLTE control implementation (common part) +// ---------------------------------------------------------------------------- + +// if MTLE is read only, no changes at all are allowed, not even from +// procedural API, in order to allow changes via API all the same we must undo +// the readonly status while we are executing, this class helps to do so + +class wxMacEditHelper +{ +public : + wxMacEditHelper( TXNObject txn ) + { + TXNControlTag tag[] = { kTXNIOPrivilegesTag } ; + m_txn = txn ; + TXNGetTXNObjectControls( m_txn , 1 , tag , m_data ) ; + if ( m_data[0].uValue == kTXNReadOnly ) + { + TXNControlData data[] = { { kTXNReadWrite } } ; + TXNSetTXNObjectControls( m_txn , false , 1 , tag , data ) ; + } + } + + ~wxMacEditHelper() + { + TXNControlTag tag[] = { kTXNIOPrivilegesTag } ; + if ( m_data[0].uValue == kTXNReadOnly ) + TXNSetTXNObjectControls( m_txn , false , 1 , tag , m_data ) ; + } + +protected : + TXNObject m_txn ; + TXNControlData m_data[1] ; +} ; + +wxMacMLTEControl::wxMacMLTEControl( wxTextCtrl *peer ) + : wxMacTextControl( peer ) +{ + SetNeedsFocusRect( true ) ; +} + +wxString wxMacMLTEControl::GetStringValue() const +{ + wxString result ; + OSStatus err ; + Size actualSize = 0; + + { +#if wxUSE_UNICODE + Handle theText ; + err = TXNGetDataEncoded( m_txn, kTXNStartOffset, kTXNEndOffset, &theText, kTXNUnicodeTextData ); + + // all done + if ( err != noErr ) + { + actualSize = 0 ; + } + else + { + actualSize = GetHandleSize( theText ) / sizeof(UniChar) ; + if ( actualSize > 0 ) + { + wxChar *ptr = NULL ; + +#if SIZEOF_WCHAR_T == 2 + ptr = new wxChar[actualSize + 1] ; + wxStrncpy( ptr , (wxChar*)(*theText) , actualSize ) ; +#else + SetHandleSize( theText, (actualSize + 1) * sizeof(UniChar) ) ; + HLock( theText ) ; + (((UniChar*)*theText)[actualSize]) = 0 ; + wxMBConvUTF16 converter ; + size_t noChars = converter.MB2WC( NULL , (const char*)*theText , 0 ) ; + wxASSERT_MSG( noChars != wxCONV_FAILED, _T("Unable to count the number of characters in this string!") ); + ptr = new wxChar[noChars + 1] ; + + noChars = converter.MB2WC( ptr , (const char*)*theText , noChars + 1 ) ; + wxASSERT_MSG( noChars != wxCONV_FAILED, _T("Conversion of string failed!") ); + ptr[noChars] = 0 ; + HUnlock( theText ) ; +#endif + + ptr[actualSize] = 0 ; + result = wxString( ptr ) ; + delete [] ptr ; + } + + DisposeHandle( theText ) ; + } +#else + Handle theText ; + err = TXNGetDataEncoded( m_txn , kTXNStartOffset, kTXNEndOffset, &theText, kTXNTextData ); + + // all done + if ( err != noErr ) + { + actualSize = 0 ; + } + else + { + actualSize = GetHandleSize( theText ) ; + if ( actualSize > 0 ) + { + HLock( theText ) ; + result = wxString( *theText , wxConvLocal , actualSize ) ; + HUnlock( theText ) ; + } + + DisposeHandle( theText ) ; + } +#endif + } + +#if '\n' == 10 + wxMacConvertNewlines13To10( &result ) ; +#else + wxMacConvertNewlines10To13( &result ) ; +#endif + + return result ; +} + +void wxMacMLTEControl::SetStringValue( const wxString &str ) +{ + wxString st = str; + wxMacConvertNewlines10To13( &st ); + + { +#ifndef __LP64__ + wxMacWindowClipper c( m_peer ) ; +#endif + + { + wxMacEditHelper help( m_txn ); + SetTXNData( st, kTXNStartOffset, kTXNEndOffset ); + } + + TXNSetSelection( m_txn, 0, 0 ); + TXNShowSelection( m_txn, kTXNShowStart ); + } +} + +TXNFrameOptions wxMacMLTEControl::FrameOptionsFromWXStyle( long wxStyle ) +{ + TXNFrameOptions frameOptions = kTXNDontDrawCaretWhenInactiveMask; + + frameOptions |= kTXNDoFontSubstitutionMask; + + if ( ! (wxStyle & wxTE_NOHIDESEL) ) + frameOptions |= kTXNDontDrawSelectionWhenInactiveMask ; + + if ( wxStyle & (wxHSCROLL | wxTE_DONTWRAP) ) + frameOptions |= kTXNWantHScrollBarMask ; + + if ( wxStyle & wxTE_MULTILINE ) + { + if ( ! (wxStyle & wxTE_DONTWRAP ) ) + frameOptions |= kTXNAlwaysWrapAtViewEdgeMask ; + + if ( !(wxStyle & wxTE_NO_VSCROLL) ) + { + frameOptions |= kTXNWantVScrollBarMask ; + + // The following code causes drawing problems on 10.4. Perhaps it can be restored for + // older versions of the OS, but I'm not sure it's appropriate to put a grow icon here + // anyways, as AFAIK users can't actually use it to resize the text ctrl. +// if ( frameOptions & kTXNWantHScrollBarMask ) +// frameOptions |= kTXNDrawGrowIconMask ; + } + } + else + { + frameOptions |= kTXNSingleLineOnlyMask ; + } + + return frameOptions ; +} + +void wxMacMLTEControl::AdjustCreationAttributes(const wxColour &background, + bool WXUNUSED(visible)) +{ + TXNControlTag iControlTags[] = + { + kTXNDoFontSubstitution, + kTXNWordWrapStateTag , + }; + TXNControlData iControlData[] = + { + { true }, + { kTXNNoAutoWrap }, + }; + + int toptag = WXSIZEOF( iControlTags ) ; + + if ( m_windowStyle & wxTE_MULTILINE ) + { + iControlData[1].uValue = + (m_windowStyle & wxTE_DONTWRAP) + ? kTXNNoAutoWrap + : kTXNAutoWrap; + } + + OSStatus err = TXNSetTXNObjectControls( m_txn, false, toptag, iControlTags, iControlData ) ; + verify_noerr( err ); + + // setting the default font: + // under 10.2 this causes a visible caret, therefore we avoid it + + Str255 fontName ; + SInt16 fontSize ; + Style fontStyle ; + + GetThemeFont( kThemeSystemFont , GetApplicationScript() , fontName , &fontSize , &fontStyle ) ; + + TXNTypeAttributes typeAttr[] = + { + { kTXNQDFontNameAttribute , kTXNQDFontNameAttributeSize , { (void*) fontName } } , + { kTXNQDFontSizeAttribute , kTXNFontSizeAttributeSize , { (void*) (fontSize << 16) } } , + { kTXNQDFontStyleAttribute , kTXNQDFontStyleAttributeSize , { (void*) normal } } , + } ; + + err = TXNSetTypeAttributes( + m_txn, sizeof(typeAttr) / sizeof(TXNTypeAttributes), + typeAttr, kTXNStartOffset, kTXNEndOffset ); + verify_noerr( err ); + + if ( m_windowStyle & wxTE_PASSWORD ) + { + UniChar c = 0x00A5 ; + err = TXNEchoMode( m_txn , c , 0 , true ); + verify_noerr( err ); + } + + TXNBackground tback; + tback.bgType = kTXNBackgroundTypeRGB; + background.GetRGBColor( &tback.bg.color ); + TXNSetBackground( m_txn , &tback ); + + + TXNCommandEventSupportOptions options ; + if ( TXNGetCommandEventSupport( m_txn, &options ) == noErr ) + { + options |= + kTXNSupportEditCommandProcessing + | kTXNSupportEditCommandUpdating + | kTXNSupportFontCommandProcessing + | kTXNSupportFontCommandUpdating; + + // only spell check when not read-only + // use system options for the default + bool checkSpelling = false ; + if ( !(m_windowStyle & wxTE_READONLY) ) + { +#if wxUSE_SYSTEM_OPTIONS + if ( wxSystemOptions::HasOption( wxMAC_TEXTCONTROL_USE_SPELL_CHECKER ) && (wxSystemOptions::GetOptionInt( wxMAC_TEXTCONTROL_USE_SPELL_CHECKER ) == 1) ) + { + checkSpelling = true ; + } +#endif + } + + if ( checkSpelling ) + options |= + kTXNSupportSpellCheckCommandProcessing + | kTXNSupportSpellCheckCommandUpdating; + + TXNSetCommandEventSupport( m_txn , options ) ; + } +} + +void wxMacMLTEControl::SetBackgroundColour(const wxColour& col ) +{ + TXNBackground tback; + tback.bgType = kTXNBackgroundTypeRGB; + col.GetRGBColor(&tback.bg.color); + TXNSetBackground( m_txn , &tback ); +} + +static inline int wxConvertToTXN(int x) +{ + return wx_static_cast(int, x / 254.0 * 72 + 0.5); +} + +void wxMacMLTEControl::TXNSetAttribute( const wxTextAttr& style , long from , long to ) +{ + TXNTypeAttributes typeAttr[4] ; + RGBColor color ; + size_t typeAttrCount = 0 ; + + TXNMargins margins; + TXNControlTag controlTags[4]; + TXNControlData controlData[4]; + size_t controlAttrCount = 0; + + TXNTab* tabs = NULL; + + bool relayout = false; + wxFont font ; + + if ( style.HasFont() ) + { + wxASSERT( typeAttrCount < WXSIZEOF(typeAttr) ); + font = style.GetFont() ; + typeAttr[typeAttrCount].tag = kTXNATSUIStyle ; + typeAttr[typeAttrCount].size = kTXNATSUIStyleSize ; + typeAttr[typeAttrCount].data.dataPtr = font.MacGetATSUStyle() ; + typeAttrCount++ ; + } + + if ( style.HasTextColour() ) + { + wxASSERT( typeAttrCount < WXSIZEOF(typeAttr) ); + style.GetTextColour().GetRGBColor( &color ); + typeAttr[typeAttrCount].tag = kTXNQDFontColorAttribute ; + typeAttr[typeAttrCount].size = kTXNQDFontColorAttributeSize ; + typeAttr[typeAttrCount].data.dataPtr = (void*) &color ; + typeAttrCount++ ; + } + + if ( style.HasAlignment() ) + { + wxASSERT( controlAttrCount < WXSIZEOF(controlTags) ); + SInt32 align; + + switch ( style.GetAlignment() ) + { + case wxTEXT_ALIGNMENT_LEFT: + align = kTXNFlushLeft; + break; + case wxTEXT_ALIGNMENT_CENTRE: + align = kTXNCenter; + break; + case wxTEXT_ALIGNMENT_RIGHT: + align = kTXNFlushRight; + break; + case wxTEXT_ALIGNMENT_JUSTIFIED: + align = kTXNFullJust; + break; + default : + case wxTEXT_ALIGNMENT_DEFAULT: + align = kTXNFlushDefault; + break; + } + + controlTags[controlAttrCount] = kTXNJustificationTag ; + controlData[controlAttrCount].sValue = align ; + controlAttrCount++ ; + } + + if ( style.HasLeftIndent() || style.HasRightIndent() ) + { + wxASSERT( controlAttrCount < WXSIZEOF(controlTags) ); + controlTags[controlAttrCount] = kTXNMarginsTag; + controlData[controlAttrCount].marginsPtr = &margins; + verify_noerr( TXNGetTXNObjectControls (m_txn, 1 , + &controlTags[controlAttrCount], &controlData[controlAttrCount]) ); + if ( style.HasLeftIndent() ) + { + margins.leftMargin = wxConvertToTXN(style.GetLeftIndent()); + } + if ( style.HasRightIndent() ) + { + margins.rightMargin = wxConvertToTXN(style.GetRightIndent()); + } + controlAttrCount++ ; + } + + if ( style.HasTabs() ) + { + const wxArrayInt& tabarray = style.GetTabs(); + // unfortunately Mac only applies a tab distance, not individually different tabs + controlTags[controlAttrCount] = kTXNTabSettingsTag; + if ( tabarray.size() > 0 ) + controlData[controlAttrCount].tabValue.value = wxConvertToTXN(tabarray[0]); + else + controlData[controlAttrCount].tabValue.value = 72 ; + + controlData[controlAttrCount].tabValue.tabType = kTXNLeftTab; + controlAttrCount++ ; + } + + // unfortunately the relayout is not automatic + if ( controlAttrCount > 0 ) + { + verify_noerr( TXNSetTXNObjectControls (m_txn, false /* don't clear all */, controlAttrCount, + controlTags, controlData) ); + relayout = true; + } + + if ( typeAttrCount > 0 ) + { + verify_noerr( TXNSetTypeAttributes( m_txn , typeAttrCount, typeAttr, from , to ) ); + relayout = true; + } + + if ( tabs != NULL ) + { + delete[] tabs; + } + + if ( relayout ) + { + TXNRecalcTextLayout( m_txn ); + } +} + +void wxMacMLTEControl::SetFont(const wxFont & font, + const wxColour& foreground, + long WXUNUSED(windowStyle)) +{ + wxMacEditHelper help( m_txn ) ; + TXNSetAttribute( wxTextAttr( foreground, wxNullColour, font ), kTXNStartOffset, kTXNEndOffset ) ; +} + +void wxMacMLTEControl::SetStyle( long start, long end, const wxTextAttr& style ) +{ + wxMacEditHelper help( m_txn ) ; + TXNSetAttribute( style, start, end ) ; +} + +void wxMacMLTEControl::Copy() +{ + TXNCopy( m_txn ); +} + +void wxMacMLTEControl::Cut() +{ + TXNCut( m_txn ); +} + +void wxMacMLTEControl::Paste() +{ + TXNPaste( m_txn ); +} + +bool wxMacMLTEControl::CanPaste() const +{ + return TXNIsScrapPastable() ; +} + +void wxMacMLTEControl::SetEditable(bool editable) +{ + TXNControlTag tag[] = { kTXNIOPrivilegesTag } ; + TXNControlData data[] = { { editable ? kTXNReadWrite : kTXNReadOnly } } ; + TXNSetTXNObjectControls( m_txn, false, WXSIZEOF(tag), tag, data ) ; +} + +wxTextPos wxMacMLTEControl::GetLastPosition() const +{ + wxTextPos actualsize = 0 ; + + Handle theText ; + OSErr err = TXNGetDataEncoded( m_txn, kTXNStartOffset, kTXNEndOffset, &theText, kTXNTextData ); + + // all done + if ( err == noErr ) + { + actualsize = GetHandleSize( theText ) ; + DisposeHandle( theText ) ; + } + else + { + actualsize = 0 ; + } + + return actualsize ; +} + +void wxMacMLTEControl::Replace( long from , long to , const wxString &str ) +{ + wxString value = str ; + wxMacConvertNewlines10To13( &value ) ; + + wxMacEditHelper help( m_txn ) ; +#ifndef __LP64__ + wxMacWindowClipper c( m_peer ) ; +#endif + + TXNSetSelection( m_txn, from, to == -1 ? kTXNEndOffset : to ) ; + TXNClear( m_txn ) ; + SetTXNData( value, kTXNUseCurrentSelection, kTXNUseCurrentSelection ) ; +} + +void wxMacMLTEControl::Remove( long from , long to ) +{ +#ifndef __LP64__ + wxMacWindowClipper c( m_peer ) ; +#endif + wxMacEditHelper help( m_txn ) ; + TXNSetSelection( m_txn , from , to ) ; + TXNClear( m_txn ) ; +} + +void wxMacMLTEControl::GetSelection( long* from, long* to) const +{ + TXNOffset f,t ; + TXNGetSelection( m_txn , &f , &t ) ; + *from = f; + *to = t; +} + +void wxMacMLTEControl::SetSelection( long from , long to ) +{ +#ifndef __LP64__ + wxMacWindowClipper c( m_peer ) ; +#endif + + // change the selection + if ((from == -1) && (to == -1)) + TXNSelectAll( m_txn ); + else + TXNSetSelection( m_txn, from, to == -1 ? kTXNEndOffset : to ); + + TXNShowSelection( m_txn, kTXNShowStart ); +} + +void wxMacMLTEControl::WriteText( const wxString& str ) +{ + wxString st = str ; + wxMacConvertNewlines10To13( &st ) ; + + long start , end , dummy ; + + GetSelection( &start , &dummy ) ; +#ifndef __LP64__ + wxMacWindowClipper c( m_peer ) ; +#endif + + { + wxMacEditHelper helper( m_txn ) ; + SetTXNData( st, kTXNUseCurrentSelection, kTXNUseCurrentSelection ) ; + } + + GetSelection( &dummy, &end ) ; + + // TODO: SetStyle( start , end , GetDefaultStyle() ) ; +} + +void wxMacMLTEControl::Clear() +{ +#ifndef __LP64__ + wxMacWindowClipper c( m_peer ) ; +#endif + wxMacEditHelper st( m_txn ) ; + TXNSetSelection( m_txn , kTXNStartOffset , kTXNEndOffset ) ; + TXNClear( m_txn ) ; +} + +bool wxMacMLTEControl::CanUndo() const +{ + return TXNCanUndo( m_txn , NULL ) ; +} + +void wxMacMLTEControl::Undo() +{ + TXNUndo( m_txn ) ; +} + +bool wxMacMLTEControl::CanRedo() const +{ + return TXNCanRedo( m_txn , NULL ) ; +} + +void wxMacMLTEControl::Redo() +{ + TXNRedo( m_txn ) ; +} + +int wxMacMLTEControl::GetNumberOfLines() const +{ + ItemCount lines = 0 ; + TXNGetLineCount( m_txn, &lines ) ; + + return lines ; +} + +long wxMacMLTEControl::XYToPosition(long x, long y) const +{ + Point curpt ; + wxTextPos lastpos ; + + // TODO: find a better implementation : while we can get the + // line metrics of a certain line, we don't get its starting + // position, so it would probably be rather a binary search + // for the start position + long xpos = 0, ypos = 0 ; + int lastHeight = 0 ; + ItemCount n ; + + lastpos = GetLastPosition() ; + for ( n = 0 ; n <= (ItemCount) lastpos ; ++n ) + { + if ( y == ypos && x == xpos ) + return n ; + + TXNOffsetToPoint( m_txn, n, &curpt ) ; + + if ( curpt.v > lastHeight ) + { + xpos = 0 ; + if ( n > 0 ) + ++ypos ; + + lastHeight = curpt.v ; + } + else + ++xpos ; + } + + return 0 ; +} + +bool wxMacMLTEControl::PositionToXY( long pos, long *x, long *y ) const +{ + Point curpt ; + wxTextPos lastpos ; + + if ( y ) + *y = 0 ; + if ( x ) + *x = 0 ; + + lastpos = GetLastPosition() ; + if ( pos <= lastpos ) + { + // TODO: find a better implementation - while we can get the + // line metrics of a certain line, we don't get its starting + // position, so it would probably be rather a binary search + // for the start position + long xpos = 0, ypos = 0 ; + int lastHeight = 0 ; + ItemCount n ; + + for ( n = 0 ; n <= (ItemCount) pos ; ++n ) + { + TXNOffsetToPoint( m_txn, n, &curpt ) ; + + if ( curpt.v > lastHeight ) + { + xpos = 0 ; + if ( n > 0 ) + ++ypos ; + + lastHeight = curpt.v ; + } + else + ++xpos ; + } + + if ( y ) + *y = ypos ; + if ( x ) + *x = xpos ; + } + + return false ; +} + +void wxMacMLTEControl::ShowPosition( long pos ) +{ + Point current, desired ; + TXNOffset selstart, selend; + + TXNGetSelection( m_txn, &selstart, &selend ); + TXNOffsetToPoint( m_txn, selstart, ¤t ); + TXNOffsetToPoint( m_txn, pos, &desired ); + + // TODO: use HIPoints for 10.3 and above + + OSErr theErr = noErr; + long dv = desired.v - current.v; + long dh = desired.h - current.h; + TXNShowSelection( m_txn, kTXNShowStart ) ; // NB: should this be kTXNShowStart or kTXNShowEnd ?? + theErr = TXNScroll( m_txn, kTXNScrollUnitsInPixels, kTXNScrollUnitsInPixels, &dv, &dh ); + + // there will be an error returned for classic MLTE implementation when the control is + // invisible, but HITextView works correctly, so we don't assert that one + // wxASSERT_MSG( theErr == noErr, _T("TXNScroll returned an error!") ); +} + +void wxMacMLTEControl::SetTXNData( const wxString& st, TXNOffset start, TXNOffset end ) +{ +#if wxUSE_UNICODE +#if SIZEOF_WCHAR_T == 2 + size_t len = st.length() ; + TXNSetData( m_txn, kTXNUnicodeTextData, (void*)st.wc_str(), len * 2, start, end ); +#else + wxMBConvUTF16 converter ; + ByteCount byteBufferLen = converter.WC2MB( NULL, st.wc_str(), 0 ) ; + UniChar *unibuf = (UniChar*)malloc( byteBufferLen ) ; + converter.WC2MB( (char*)unibuf, st.wc_str(), byteBufferLen ) ; + TXNSetData( m_txn, kTXNUnicodeTextData, (void*)unibuf, byteBufferLen, start, end ) ; + free( unibuf ) ; +#endif +#else + wxCharBuffer text = st.mb_str( wxConvLocal ) ; + TXNSetData( m_txn, kTXNTextData, (void*)text.data(), strlen( text ), start, end ) ; +#endif +} + +wxString wxMacMLTEControl::GetLineText(long lineNo) const +{ + wxString line ; + + if ( lineNo < GetNumberOfLines() ) + { + Point firstPoint; + Fixed lineWidth, lineHeight, currentHeight; + long ypos ; + + // get the first possible position in the control + TXNOffsetToPoint(m_txn, 0, &firstPoint); + + // Iterate through the lines until we reach the one we want, + // adding to our current y pixel point position + ypos = 0 ; + currentHeight = 0; + while (ypos < lineNo) + { + TXNGetLineMetrics(m_txn, ypos++, &lineWidth, &lineHeight); + currentHeight += lineHeight; + } + + Point thePoint = { firstPoint.v + (currentHeight >> 16), firstPoint.h + (0) }; + TXNOffset theOffset; + TXNPointToOffset(m_txn, thePoint, &theOffset); + + wxString content = GetStringValue() ; + Point currentPoint = thePoint; + while (thePoint.v == currentPoint.v && theOffset < content.length()) + { + line += content[theOffset]; + TXNOffsetToPoint(m_txn, ++theOffset, ¤tPoint); + } + } + + return line ; +} + +int wxMacMLTEControl::GetLineLength(long lineNo) const +{ + int theLength = 0; + + if ( lineNo < GetNumberOfLines() ) + { + Point firstPoint; + Fixed lineWidth, lineHeight, currentHeight; + long ypos; + + // get the first possible position in the control + TXNOffsetToPoint(m_txn, 0, &firstPoint); + + // Iterate through the lines until we reach the one we want, + // adding to our current y pixel point position + ypos = 0; + currentHeight = 0; + while (ypos < lineNo) + { + TXNGetLineMetrics(m_txn, ypos++, &lineWidth, &lineHeight); + currentHeight += lineHeight; + } + + Point thePoint = { firstPoint.v + (currentHeight >> 16), firstPoint.h + (0) }; + TXNOffset theOffset; + TXNPointToOffset(m_txn, thePoint, &theOffset); + + wxString content = GetStringValue() ; + Point currentPoint = thePoint; + while (thePoint.v == currentPoint.v && theOffset < content.length()) + { + ++theLength; + TXNOffsetToPoint(m_txn, ++theOffset, ¤tPoint); + } + } + + return theLength ; +} + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + +// ---------------------------------------------------------------------------- +// MLTE control implementation (classic part) +// ---------------------------------------------------------------------------- + +// OS X Notes : We still don't have a full replacement for MLTE, so this implementation +// has to live on. We have different problems coming from outdated implementations on the +// various OS X versions. Most deal with the scrollbars: they are not correctly embedded +// while this can be solved on 10.3 by reassigning them the correct place, on 10.2 there is +// no way out, therefore we are using our own implementation and our own scrollbars .... + +TXNScrollInfoUPP gTXNScrollInfoProc = NULL ; +ControlActionUPP gTXNScrollActionProc = NULL ; + +pascal void wxMacMLTEClassicControl::TXNScrollInfoProc( + SInt32 iValue, SInt32 iMaximumValue, + TXNScrollBarOrientation iScrollBarOrientation, SInt32 iRefCon ) +{ + wxMacMLTEClassicControl* mlte = (wxMacMLTEClassicControl*) iRefCon ; + SInt32 value = wxMax( iValue , 0 ) ; + SInt32 maximum = wxMax( iMaximumValue , 0 ) ; + + if ( iScrollBarOrientation == kTXNHorizontal ) + { + if ( mlte->m_sbHorizontal ) + { + SetControl32BitValue( mlte->m_sbHorizontal , value ) ; + SetControl32BitMaximum( mlte->m_sbHorizontal , maximum ) ; + mlte->m_lastHorizontalValue = value ; + } + } + else if ( iScrollBarOrientation == kTXNVertical ) + { + if ( mlte->m_sbVertical ) + { + SetControl32BitValue( mlte->m_sbVertical , value ) ; + SetControl32BitMaximum( mlte->m_sbVertical , maximum ) ; + mlte->m_lastVerticalValue = value ; + } + } +} + +pascal void wxMacMLTEClassicControl::TXNScrollActionProc( ControlRef controlRef , ControlPartCode partCode ) +{ + wxMacMLTEClassicControl* mlte = (wxMacMLTEClassicControl*) GetControlReference( controlRef ) ; + if ( mlte == NULL ) + return ; + + if ( controlRef != mlte->m_sbVertical && controlRef != mlte->m_sbHorizontal ) + return ; + + OSStatus err ; + bool isHorizontal = ( controlRef == mlte->m_sbHorizontal ) ; + + SInt32 minimum = 0 ; + SInt32 maximum = GetControl32BitMaximum( controlRef ) ; + SInt32 value = GetControl32BitValue( controlRef ) ; + SInt32 delta = 0; + + switch ( partCode ) + { + case kControlDownButtonPart : + delta = 10 ; + break ; + + case kControlUpButtonPart : + delta = -10 ; + break ; + + case kControlPageDownPart : + delta = GetControlViewSize( controlRef ) ; + break ; + + case kControlPageUpPart : + delta = -GetControlViewSize( controlRef ) ; + break ; + + case kControlIndicatorPart : + delta = value - (isHorizontal ? mlte->m_lastHorizontalValue : mlte->m_lastVerticalValue) ; + break ; + + default : + break ; + } + + if ( delta != 0 ) + { + SInt32 newValue = value ; + + if ( partCode != kControlIndicatorPart ) + { + if ( value + delta < minimum ) + delta = minimum - value ; + if ( value + delta > maximum ) + delta = maximum - value ; + + SetControl32BitValue( controlRef , value + delta ) ; + newValue = value + delta ; + } + + SInt32 verticalDelta = isHorizontal ? 0 : delta ; + SInt32 horizontalDelta = isHorizontal ? delta : 0 ; + + err = TXNScroll( + mlte->m_txn, kTXNScrollUnitsInPixels, kTXNScrollUnitsInPixels, + &verticalDelta, &horizontalDelta ); + verify_noerr( err ); + + if ( isHorizontal ) + mlte->m_lastHorizontalValue = newValue ; + else + mlte->m_lastVerticalValue = newValue ; + } +} + +// make correct activations +void wxMacMLTEClassicControl::MacActivatePaneText(bool setActive) +{ + wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference(m_controlRef); + + wxMacWindowClipper clipper( textctrl ) ; + TXNActivate( m_txn, m_txnFrameID, setActive ); + + ControlRef controlFocus = 0 ; + GetKeyboardFocus( m_txnWindow , &controlFocus ) ; + if ( controlFocus == m_controlRef ) + TXNFocus( m_txn, setActive ); +} + +void wxMacMLTEClassicControl::MacFocusPaneText(bool setFocus) +{ + TXNFocus( m_txn, setFocus ); +} + +// guards against inappropriate redraw (hidden objects drawing onto window) + +void wxMacMLTEClassicControl::MacSetObjectVisibility(bool vis) +{ + ControlRef controlFocus = 0 ; + GetKeyboardFocus( m_txnWindow , &controlFocus ) ; + + if ( !vis && (controlFocus == m_controlRef ) ) + SetKeyboardFocus( m_txnWindow , m_controlRef , kControlFocusNoPart ) ; + + TXNControlTag iControlTags[1] = { kTXNVisibilityTag }; + TXNControlData iControlData[1] = { { (UInt32)false } }; + + verify_noerr( TXNGetTXNObjectControls( m_txn , 1, iControlTags, iControlData ) ) ; + + if ( iControlData[0].uValue != vis ) + { + iControlData[0].uValue = vis ; + verify_noerr( TXNSetTXNObjectControls( m_txn, false , 1, iControlTags, iControlData ) ) ; + } + + // currently, we always clip as partial visibility (overlapped) visibility is also a problem, + // if we run into further problems we might set the FrameBounds to an empty rect here +} + +// make sure that the TXNObject is at the right position + +void wxMacMLTEClassicControl::MacUpdatePosition() +{ + wxTextCtrl* textctrl = (wxTextCtrl*)GetControlReference( m_controlRef ); + if ( textctrl == NULL ) + return ; + + Rect bounds ; + GetRectInWindowCoords( &bounds ); + + wxRect visRect = textctrl->MacGetClippedClientRect() ; + Rect visBounds = { visRect.y , visRect.x , visRect.y + visRect.height , visRect.x + visRect.width } ; + int x , y ; + x = y = 0 ; + textctrl->MacWindowToRootWindow( &x , &y ) ; + OffsetRect( &visBounds , x , y ) ; + + if ( !EqualRect( &bounds, &m_txnControlBounds ) || !EqualRect( &visBounds, &m_txnVisBounds ) ) + { + m_txnControlBounds = bounds ; + m_txnVisBounds = visBounds ; + wxMacWindowClipper cl( textctrl ) ; + + if ( m_sbHorizontal || m_sbVertical ) + { + int w = bounds.right - bounds.left ; + int h = bounds.bottom - bounds.top ; + + if ( m_sbHorizontal ) + { + Rect sbBounds ; + + sbBounds.left = -1 ; + sbBounds.top = h - 14 ; + sbBounds.right = w + 1 ; + sbBounds.bottom = h + 1 ; + + SetControlBounds( m_sbHorizontal , &sbBounds ) ; + SetControlViewSize( m_sbHorizontal , w ) ; + } + + if ( m_sbVertical ) + { + Rect sbBounds ; + + sbBounds.left = w - 14 ; + sbBounds.top = -1 ; + sbBounds.right = w + 1 ; + sbBounds.bottom = m_sbHorizontal ? h - 14 : h + 1 ; + + SetControlBounds( m_sbVertical , &sbBounds ) ; + SetControlViewSize( m_sbVertical , h ) ; + } + } + + Rect oldviewRect ; + TXNLongRect olddestRect ; + TXNGetRectBounds( m_txn , &oldviewRect , &olddestRect , NULL ) ; + + Rect viewRect = { m_txnControlBounds.top, m_txnControlBounds.left, + m_txnControlBounds.bottom - ( m_sbHorizontal ? 14 : 0 ) , + m_txnControlBounds.right - ( m_sbVertical ? 14 : 0 ) } ; + TXNLongRect destRect = { m_txnControlBounds.top, m_txnControlBounds.left, + m_txnControlBounds.bottom - ( m_sbHorizontal ? 14 : 0 ) , + m_txnControlBounds.right - ( m_sbVertical ? 14 : 0 ) } ; + + if ( olddestRect.right >= 10000 ) + destRect.right = destRect.left + 32000 ; + + if ( olddestRect.bottom >= 0x20000000 ) + destRect.bottom = destRect.top + 0x40000000 ; + + SectRect( &viewRect , &visBounds , &viewRect ) ; + TXNSetRectBounds( m_txn , &viewRect , &destRect , true ) ; + +#if 0 + TXNSetFrameBounds( + m_txn, + m_txnControlBounds.top, + m_txnControlBounds.left, + m_txnControlBounds.bottom - (m_sbHorizontal ? 14 : 0), + m_txnControlBounds.right - (m_sbVertical ? 14 : 0), + m_txnFrameID ); +#endif + + // the SetFrameBounds method under Classic sometimes does not correctly scroll a selection into sight after a + // movement, therefore we have to force it + + // this problem has been reported in OSX as well, so we use this here once again + + TXNLongRect textRect ; + TXNGetRectBounds( m_txn , NULL , NULL , &textRect ) ; + if ( textRect.left < m_txnControlBounds.left ) + TXNShowSelection( m_txn , kTXNShowStart ) ; + } +} + +void wxMacMLTEClassicControl::SetRect( Rect *r ) +{ + wxMacControl::SetRect( r ) ; + MacUpdatePosition() ; +} + +void wxMacMLTEClassicControl::MacControlUserPaneDrawProc(wxInt16 WXUNUSED(thePart)) +{ + wxTextCtrl* textctrl = (wxTextCtrl*)GetControlReference( m_controlRef ); + if ( textctrl == NULL ) + return ; + + if ( textctrl->IsShownOnScreen() ) + { + wxMacWindowClipper clipper( textctrl ) ; + TXNDraw( m_txn , NULL ) ; + } +} + +wxInt16 wxMacMLTEClassicControl::MacControlUserPaneHitTestProc(wxInt16 x, wxInt16 y) +{ + Point where = { y , x } ; + ControlPartCode result = kControlNoPart; + + wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference( m_controlRef ); + if ( (textctrl != NULL) && textctrl->IsShownOnScreen() ) + { + if (PtInRect( where, &m_txnControlBounds )) + { + result = kControlEditTextPart ; + } + else + { + // sometimes we get the coords also in control local coordinates, therefore test again + int x = 0 , y = 0 ; + textctrl->MacClientToRootWindow( &x , &y ) ; + where.h += x ; + where.v += y ; + + if (PtInRect( where, &m_txnControlBounds )) + result = kControlEditTextPart ; + } + } + + return result; +} + +wxInt16 wxMacMLTEClassicControl::MacControlUserPaneTrackingProc( wxInt16 x, wxInt16 y, void* WXUNUSED(actionProc) ) +{ + ControlPartCode result = kControlNoPart; + + wxTextCtrl* textctrl = (wxTextCtrl*) GetControlReference( m_controlRef ); + if ( (textctrl != NULL) && textctrl->IsShownOnScreen() ) + { + Point startPt = { y , x } ; + + // for compositing, we must convert these into toplevel window coordinates, because hittesting expects them + int x = 0 , y = 0 ; + textctrl->MacClientToRootWindow( &x , &y ) ; + startPt.h += x ; + startPt.v += y ; + + switch (MacControlUserPaneHitTestProc( startPt.h , startPt.v )) + { + case kControlEditTextPart : + { + wxMacWindowClipper clipper( textctrl ) ; + EventRecord rec ; + + ConvertEventRefToEventRecord( (EventRef) wxTheApp->MacGetCurrentEvent() , &rec ) ; + TXNClick( m_txn, &rec ); + } + break; + + default : + break; + } + } + + return result; +} + +void wxMacMLTEClassicControl::MacControlUserPaneIdleProc() +{ + wxTextCtrl* textctrl = (wxTextCtrl*)GetControlReference( m_controlRef ); + if ( textctrl == NULL ) + return ; + + if (textctrl->IsShownOnScreen()) + { + if (IsControlActive(m_controlRef)) + { + Point mousep; + + wxMacWindowClipper clipper( textctrl ) ; + GetMouse(&mousep); + + TXNIdle(m_txn); + + if (PtInRect(mousep, &m_txnControlBounds)) + { + RgnHandle theRgn = NewRgn(); + RectRgn(theRgn, &m_txnControlBounds); + TXNAdjustCursor(m_txn, theRgn); + DisposeRgn(theRgn); + } + } + } +} + +wxInt16 wxMacMLTEClassicControl::MacControlUserPaneKeyDownProc (wxInt16 keyCode, wxInt16 charCode, wxInt16 modifiers) +{ + wxTextCtrl* textctrl = (wxTextCtrl*)GetControlReference( m_controlRef ); + if ( textctrl == NULL ) + return kControlNoPart; + + wxMacWindowClipper clipper( textctrl ) ; + + EventRecord ev ; + memset( &ev , 0 , sizeof( ev ) ) ; + ev.what = keyDown ; + ev.modifiers = modifiers ; + ev.message = ((keyCode << 8) & keyCodeMask) | (charCode & charCodeMask); + TXNKeyDown( m_txn , &ev ); + + return kControlEntireControl; +} + +void wxMacMLTEClassicControl::MacControlUserPaneActivateProc(bool activating) +{ + MacActivatePaneText( activating ); +} + +wxInt16 wxMacMLTEClassicControl::MacControlUserPaneFocusProc(wxInt16 action) +{ + ControlPartCode focusResult = kControlFocusNoPart; + + wxTextCtrl* textctrl = (wxTextCtrl*)GetControlReference( m_controlRef ); + if ( textctrl == NULL ) + return focusResult; + + wxMacWindowClipper clipper( textctrl ) ; + + ControlRef controlFocus = NULL ; + GetKeyboardFocus( m_txnWindow , &controlFocus ) ; + bool wasFocused = ( controlFocus == m_controlRef ) ; + + switch (action) + { + case kControlFocusPrevPart: + case kControlFocusNextPart: + MacFocusPaneText( !wasFocused ); + focusResult = (!wasFocused ? (ControlPartCode) kControlEditTextPart : (ControlPartCode) kControlFocusNoPart); + break; + + case kControlFocusNoPart: + default: + MacFocusPaneText( false ); + focusResult = kControlFocusNoPart; + break; + } + + return focusResult; +} + +void wxMacMLTEClassicControl::MacControlUserPaneBackgroundProc( void *WXUNUSED(info) ) +{ +} + +wxMacMLTEClassicControl::wxMacMLTEClassicControl( wxTextCtrl *wxPeer, + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) + : wxMacMLTEControl( wxPeer ) +{ + m_font = wxPeer->GetFont() ; + m_windowStyle = style ; + Rect bounds = wxMacGetBoundsForControl( wxPeer , pos , size ) ; + + short featureSet = + kControlSupportsEmbedding | kControlSupportsFocus | kControlWantsIdle + | kControlWantsActivate | kControlHandlesTracking +// | kControlHasSpecialBackground + | kControlGetsFocusOnClick | kControlSupportsLiveFeedback; + + OSStatus err = ::CreateUserPaneControl( + MAC_WXHWND(wxPeer->GetParent()->MacGetTopLevelWindowRef()), + &bounds, featureSet, &m_controlRef ); + verify_noerr( err ); + + DoCreate(); + + AdjustCreationAttributes( *wxWHITE , true ) ; + + MacSetObjectVisibility( wxPeer->IsShownOnScreen() ) ; + + { + wxString st = str ; + wxMacConvertNewlines10To13( &st ) ; + wxMacWindowClipper clipper( m_peer ) ; + SetTXNData( st , kTXNStartOffset, kTXNEndOffset ) ; + TXNSetSelection( m_txn, 0, 0 ) ; + } +} + +wxMacMLTEClassicControl::~wxMacMLTEClassicControl() +{ + TXNDeleteObject( m_txn ); + m_txn = NULL ; +} + +void wxMacMLTEClassicControl::VisibilityChanged(bool shown) +{ + MacSetObjectVisibility( shown ) ; + wxMacControl::VisibilityChanged( shown ) ; +} + +void wxMacMLTEClassicControl::SuperChangedPosition() +{ + MacUpdatePosition() ; + wxMacControl::SuperChangedPosition() ; +} + +ControlUserPaneDrawUPP gTPDrawProc = NULL; +ControlUserPaneHitTestUPP gTPHitProc = NULL; +ControlUserPaneTrackingUPP gTPTrackProc = NULL; +ControlUserPaneIdleUPP gTPIdleProc = NULL; +ControlUserPaneKeyDownUPP gTPKeyProc = NULL; +ControlUserPaneActivateUPP gTPActivateProc = NULL; +ControlUserPaneFocusUPP gTPFocusProc = NULL; + +static pascal void wxMacControlUserPaneDrawProc(ControlRef control, SInt16 part) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + win->MacControlUserPaneDrawProc( part ) ; +} + +static pascal ControlPartCode wxMacControlUserPaneHitTestProc(ControlRef control, Point where) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + return win->MacControlUserPaneHitTestProc( where.h , where.v ) ; + else + return kControlNoPart ; +} + +static pascal ControlPartCode wxMacControlUserPaneTrackingProc(ControlRef control, Point startPt, ControlActionUPP actionProc) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + return win->MacControlUserPaneTrackingProc( startPt.h , startPt.v , (void*) actionProc ) ; + else + return kControlNoPart ; +} + +static pascal void wxMacControlUserPaneIdleProc(ControlRef control) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + win->MacControlUserPaneIdleProc() ; +} + +static pascal ControlPartCode wxMacControlUserPaneKeyDownProc(ControlRef control, SInt16 keyCode, SInt16 charCode, SInt16 modifiers) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + return win->MacControlUserPaneKeyDownProc( keyCode, charCode, modifiers ) ; + else + return kControlNoPart ; +} + +static pascal void wxMacControlUserPaneActivateProc(ControlRef control, Boolean activating) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + win->MacControlUserPaneActivateProc( activating ) ; +} + +static pascal ControlPartCode wxMacControlUserPaneFocusProc(ControlRef control, ControlFocusPart action) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + return win->MacControlUserPaneFocusProc( action ) ; + else + return kControlNoPart ; +} + +#if 0 +static pascal void wxMacControlUserPaneBackgroundProc(ControlRef control, ControlBackgroundPtr info) +{ + wxTextCtrl *textCtrl = wxDynamicCast( wxFindControlFromMacControl(control) , wxTextCtrl ) ; + wxMacMLTEClassicControl * win = textCtrl ? (wxMacMLTEClassicControl*)(textCtrl->GetPeer()) : NULL ; + if ( win ) + win->MacControlUserPaneBackgroundProc(info) ; +} +#endif + +// TXNRegisterScrollInfoProc + +OSStatus wxMacMLTEClassicControl::DoCreate() +{ + Rect bounds; + OSStatus err = noErr ; + + // set up our globals + if (gTPDrawProc == NULL) gTPDrawProc = NewControlUserPaneDrawUPP(wxMacControlUserPaneDrawProc); + if (gTPHitProc == NULL) gTPHitProc = NewControlUserPaneHitTestUPP(wxMacControlUserPaneHitTestProc); + if (gTPTrackProc == NULL) gTPTrackProc = NewControlUserPaneTrackingUPP(wxMacControlUserPaneTrackingProc); + if (gTPIdleProc == NULL) gTPIdleProc = NewControlUserPaneIdleUPP(wxMacControlUserPaneIdleProc); + if (gTPKeyProc == NULL) gTPKeyProc = NewControlUserPaneKeyDownUPP(wxMacControlUserPaneKeyDownProc); + if (gTPActivateProc == NULL) gTPActivateProc = NewControlUserPaneActivateUPP(wxMacControlUserPaneActivateProc); + if (gTPFocusProc == NULL) gTPFocusProc = NewControlUserPaneFocusUPP(wxMacControlUserPaneFocusProc); + + if (gTXNScrollInfoProc == NULL ) gTXNScrollInfoProc = NewTXNScrollInfoUPP(TXNScrollInfoProc) ; + if (gTXNScrollActionProc == NULL ) gTXNScrollActionProc = NewControlActionUPP(TXNScrollActionProc) ; + + // set the initial settings for our private data + + m_txnWindow = GetControlOwner(m_controlRef); + m_txnPort = (GrafPtr) GetWindowPort(m_txnWindow); + + // set up the user pane procedures + SetControlData(m_controlRef, kControlEntireControl, kControlUserPaneDrawProcTag, sizeof(gTPDrawProc), &gTPDrawProc); + SetControlData(m_controlRef, kControlEntireControl, kControlUserPaneHitTestProcTag, sizeof(gTPHitProc), &gTPHitProc); + SetControlData(m_controlRef, kControlEntireControl, kControlUserPaneTrackingProcTag, sizeof(gTPTrackProc), &gTPTrackProc); + SetControlData(m_controlRef, kControlEntireControl, kControlUserPaneIdleProcTag, sizeof(gTPIdleProc), &gTPIdleProc); + SetControlData(m_controlRef, kControlEntireControl, kControlUserPaneKeyDownProcTag, sizeof(gTPKeyProc), &gTPKeyProc); + SetControlData(m_controlRef, kControlEntireControl, kControlUserPaneActivateProcTag, sizeof(gTPActivateProc), &gTPActivateProc); + SetControlData(m_controlRef, kControlEntireControl, kControlUserPaneFocusProcTag, sizeof(gTPFocusProc), &gTPFocusProc); + + // calculate the rectangles used by the control + GetRectInWindowCoords( &bounds ); + + m_txnControlBounds = bounds ; + m_txnVisBounds = bounds ; + + CGrafPtr origPort ; + GDHandle origDev ; + + GetGWorld( &origPort, &origDev ) ; + SetPort( m_txnPort ); + + // create the new edit field + TXNFrameOptions frameOptions = FrameOptionsFromWXStyle( m_windowStyle ); + + // the scrollbars are not correctly embedded but are inserted at the root: + // this gives us problems as we have erratic redraws even over the structure area + + m_sbHorizontal = 0 ; + m_sbVertical = 0 ; + m_lastHorizontalValue = 0 ; + m_lastVerticalValue = 0 ; + + Rect sb = { 0 , 0 , 0 , 0 } ; + if ( frameOptions & kTXNWantVScrollBarMask ) + { + CreateScrollBarControl( m_txnWindow, &sb, 0, 0, 100, 1, true, gTXNScrollActionProc, &m_sbVertical ); + SetControlReference( m_sbVertical, (SInt32)this ); + SetControlAction( m_sbVertical, gTXNScrollActionProc ); + ShowControl( m_sbVertical ); + EmbedControl( m_sbVertical , m_controlRef ); + frameOptions &= ~kTXNWantVScrollBarMask; + } + + if ( frameOptions & kTXNWantHScrollBarMask ) + { + CreateScrollBarControl( m_txnWindow, &sb, 0, 0, 100, 1, true, gTXNScrollActionProc, &m_sbHorizontal ); + SetControlReference( m_sbHorizontal, (SInt32)this ); + SetControlAction( m_sbHorizontal, gTXNScrollActionProc ); + ShowControl( m_sbHorizontal ); + EmbedControl( m_sbHorizontal, m_controlRef ); + frameOptions &= ~(kTXNWantHScrollBarMask | kTXNDrawGrowIconMask); + } + + err = TXNNewObject( + NULL, m_txnWindow, &bounds, frameOptions, + kTXNTextEditStyleFrameType, kTXNTextensionFile, kTXNSystemDefaultEncoding, + &m_txn, &m_txnFrameID, NULL ); + verify_noerr( err ); + +#if 0 + TXNControlTag iControlTags[] = { kTXNUseCarbonEvents }; + TXNControlData iControlData[] = { { (UInt32)&cInfo } }; + int toptag = WXSIZEOF( iControlTags ) ; + TXNCarbonEventInfo cInfo ; + cInfo.useCarbonEvents = false ; + cInfo.filler = 0 ; + cInfo.flags = 0 ; + cInfo.fDictionary = NULL ; + + verify_noerr( TXNSetTXNObjectControls( m_txn, false, toptag, iControlTags, iControlData ) ); +#endif + + TXNRegisterScrollInfoProc( m_txn, gTXNScrollInfoProc, (SInt32)this ); + + SetGWorld( origPort , origDev ) ; + + return err; +} +#endif + +// ---------------------------------------------------------------------------- +// MLTE control implementation (OSX part) +// ---------------------------------------------------------------------------- + +// tiger multi-line textcontrols with no CR in the entire content +// don't scroll automatically, so we need a hack. +// This attempt only works 'before' the key (ie before CallNextEventHandler) +// is processed, thus the scrolling always occurs one character too late, but +// better than nothing ... + +static const EventTypeSpec eventList[] = +{ + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } , +} ; + +static pascal OSStatus wxMacUnicodeTextEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + wxMacMLTEHIViewControl* focus = (wxMacMLTEHIViewControl*) data ; + + switch ( GetEventKind( event ) ) + { + case kEventTextInputUnicodeForKeyEvent : + { + TXNOffset from , to ; + TXNGetSelection( focus->GetTXNObject() , &from , &to ) ; + if ( from == to ) + TXNShowSelection( focus->GetTXNObject() , kTXNShowStart ); + result = CallNextEventHandler(handler,event); + break; + } + default: + break ; + } + + return result ; +} + +static pascal OSStatus wxMacTextControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + + switch ( GetEventClass( event ) ) + { + case kEventClassTextInput : + result = wxMacUnicodeTextEventHandler( handler , event , data ) ; + break ; + + default : + break ; + } + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacTextControlEventHandler ) + +wxMacMLTEHIViewControl::wxMacMLTEHIViewControl( wxTextCtrl *wxPeer, + const wxString& str, + const wxPoint& pos, + const wxSize& size, long style ) : wxMacMLTEControl( wxPeer ) +{ + m_font = wxPeer->GetFont() ; + m_windowStyle = style ; + Rect bounds = wxMacGetBoundsForControl( wxPeer , pos , size ) ; + wxString st = str ; + wxMacConvertNewlines10To13( &st ) ; + + HIRect hr = { + { bounds.left , bounds.top }, + { bounds.right - bounds.left, bounds.bottom - bounds.top } } ; + + m_scrollView = NULL ; + TXNFrameOptions frameOptions = FrameOptionsFromWXStyle( style ) ; + if (( frameOptions & (kTXNWantVScrollBarMask | kTXNWantHScrollBarMask)) || (frameOptions &kTXNSingleLineOnlyMask)) + { + if ( frameOptions & (kTXNWantVScrollBarMask | kTXNWantHScrollBarMask) ) + { + HIScrollViewCreate( + (frameOptions & kTXNWantHScrollBarMask ? kHIScrollViewOptionsHorizScroll : 0) + | (frameOptions & kTXNWantVScrollBarMask ? kHIScrollViewOptionsVertScroll : 0) , + &m_scrollView ) ; + } + else + { + HIScrollViewCreate(kHIScrollViewOptionsVertScroll,&m_scrollView); + HIScrollViewSetScrollBarAutoHide(m_scrollView,true); + } + + HIViewSetFrame( m_scrollView, &hr ); + HIViewSetVisible( m_scrollView, true ); + } + + m_textView = NULL ; + HITextViewCreate( NULL , 0, frameOptions , &m_textView ) ; + m_txn = HITextViewGetTXNObject( m_textView ) ; + HIViewSetVisible( m_textView , true ) ; + if ( m_scrollView ) + { + HIViewAddSubview( m_scrollView , m_textView ) ; + m_controlRef = m_scrollView ; + wxPeer->MacInstallEventHandler( (WXWidget) m_textView ) ; + } + else + { + HIViewSetFrame( m_textView, &hr ); + m_controlRef = m_textView ; + } + + AdjustCreationAttributes( *wxWHITE , true ) ; +#ifndef __LP64__ + wxMacWindowClipper c( m_peer ) ; +#endif + SetTXNData( st , kTXNStartOffset, kTXNEndOffset ) ; + + TXNSetSelection( m_txn, 0, 0 ); + TXNShowSelection( m_txn, kTXNShowStart ); + + InstallControlEventHandler( m_textView , GetwxMacTextControlEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, this, + NULL); +} + +wxMacMLTEHIViewControl::~wxMacMLTEHIViewControl() +{ +} + +OSStatus wxMacMLTEHIViewControl::SetFocus( ControlFocusPart focusPart ) +{ + return SetKeyboardFocus( GetControlOwner( m_textView ), m_textView, focusPart ) ; +} + +bool wxMacMLTEHIViewControl::HasFocus() const +{ + ControlRef control ; + if ( GetUserFocusWindow() == NULL ) + return false; + + GetKeyboardFocus( GetUserFocusWindow() , &control ) ; + return control == m_textView ; +} + +void wxMacMLTEHIViewControl::SetBackgroundColour(const wxColour& col ) +{ + HITextViewSetBackgroundColor( m_textView, col.GetPixel() ); +} + +#endif // wxUSE_TEXTCTRL diff --git a/src/osx/carbon/tglbtn.cpp b/src/osx/carbon/tglbtn.cpp new file mode 100644 index 0000000000..43904685f7 --- /dev/null +++ b/src/osx/carbon/tglbtn.cpp @@ -0,0 +1,174 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/tglbtn.cpp +// Purpose: Definition of the wxToggleButton class, which implements a +// toggle button under wxMac. +// Author: Stefan Csomor +// Modified by: +// Created: 08.02.01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// License: wxWindows license +///////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declatations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "wx/wxprec.h" + +#if wxUSE_TOGGLEBTN + +#include "wx/tglbtn.h" +#include "wx/mac/uma.h" +// Button + +// ---------------------------------------------------------------------------- +// macros +// ---------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxToggleButton, wxControl) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED) + +// ============================================================================ +// implementation +// ============================================================================ +// ---------------------------------------------------------------------------- +// wxToggleButton +// ---------------------------------------------------------------------------- + +bool wxToggleButton::Create(wxWindow *parent, wxWindowID id, + const wxString& label, + const wxPoint& pos, + const wxSize& size, long style, + const wxValidator& validator, + const wxString& name) +{ + m_macIsUserPane = FALSE ; + + if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) + return false; + + m_labelOrig = m_label = label ; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ; + + m_peer = new wxMacControl(this) ; + verify_noerr ( CreateBevelButtonControl( MAC_WXHWND(parent->MacGetTopLevelWindowRef()) , &bounds , CFSTR("") , + kControlBevelButtonNormalBevel , kControlBehaviorToggles , NULL , 0 , 0 , 0 , m_peer->GetControlRefAddr() ) ); + + + MacPostControlCreate(pos,size) ; + + return TRUE; +} + +wxSize wxToggleButton::DoGetBestSize() const +{ + int wBtn = 70 ; + int hBtn = 20 ; + + int lBtn = m_label.Length() * 8 + 12 ; + if (lBtn > wBtn) + wBtn = lBtn; + + return wxSize ( wBtn , hBtn ) ; +} + +void wxToggleButton::SetValue(bool val) +{ + m_peer->SetValue( val ) ; +} + +bool wxToggleButton::GetValue() const +{ + return m_peer->GetValue() ; +} + +void wxToggleButton::Command(wxCommandEvent & event) +{ + SetValue((event.GetInt() != 0)); + ProcessCommand(event); +} + +wxInt32 wxToggleButton::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCommandEvent event(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, m_windowId); + event.SetInt(GetValue()); + event.SetEventObject(this); + ProcessCommand(event); + return noErr ; +} + +// ---------------------------------------------------------------------------- +// wxBitmapToggleButton +// ---------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxBitmapToggleButton, wxControl) + +bool wxBitmapToggleButton::Create(wxWindow *parent, wxWindowID id, + const wxBitmap& label, + const wxPoint& pos, + const wxSize& size, long style, + const wxValidator& validator, + const wxString& name) +{ + m_macIsUserPane = FALSE ; + + m_bitmap = label; + + if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) + return false; + + Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ; + + m_peer = new wxMacControl(this) ; + + ControlButtonContentInfo info; + wxMacCreateBitmapButton( &info, m_bitmap ); + verify_noerr ( CreateBevelButtonControl( MAC_WXHWND(parent->MacGetTopLevelWindowRef()) , &bounds , CFSTR("") , + kControlBevelButtonNormalBevel , kControlBehaviorOffsetContents | kControlBehaviorToggles , &info , 0 , 0 , 0 , m_peer->GetControlRefAddr() ) ); + + MacPostControlCreate(pos,size) ; + + return TRUE; +} + +wxSize wxBitmapToggleButton::DoGetBestSize() const +{ + if (!m_bitmap.IsOk()) + return wxSize(20,20); + + return wxSize ( m_bitmap.GetWidth()+6, m_bitmap.GetHeight()+6 ) ; +} + +void wxBitmapToggleButton::SetValue(bool val) +{ + m_peer->SetValue( val ) ; +} + +bool wxBitmapToggleButton::GetValue() const +{ + return m_peer->GetValue() ; +} + +void wxBitmapToggleButton::Command(wxCommandEvent & event) +{ + SetValue((event.GetInt() != 0)); + ProcessCommand(event); +} + +wxInt32 wxBitmapToggleButton::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + wxCommandEvent event(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, m_windowId); + event.SetInt(GetValue()); + event.SetEventObject(this); + ProcessCommand(event); + return noErr ; +} + +#endif // wxUSE_TOGGLEBTN + diff --git a/src/osx/carbon/thread.cpp b/src/osx/carbon/thread.cpp new file mode 100644 index 0000000000..04383b0f94 --- /dev/null +++ b/src/osx/carbon/thread.cpp @@ -0,0 +1,1774 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/thread.cpp +// Purpose: wxThread Implementation +// Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin +// Modified by: Aj Lavin, Stefan Csomor +// Created: 04/22/98 +// RCS-ID: $Id$ +// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998), +// Vadim Zeitlin (1999), Stefan Csomor (2000) +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if defined(__BORLANDC__) + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" + #include "wx/module.h" +#endif + +#if wxUSE_THREADS + +#include "wx/thread.h" + +#include +#include "wx/mac/uma.h" + +// the possible states of the thread: +// ("=>" shows all possible transitions from this state) +enum wxThreadState +{ + STATE_NEW, // didn't start execution yet (=> RUNNING) + STATE_RUNNING, // thread is running (=> PAUSED, CANCELED) + STATE_PAUSED, // thread is temporarily suspended (=> RUNNING) + STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED) + STATE_EXITED // thread is terminating +}; + +// ---------------------------------------------------------------------------- +// globals +// ---------------------------------------------------------------------------- + +// the task ID of the main thread +static wxThreadIdType gs_idMainThread = kInvalidID; + +// this is the Per-Task Storage for the pointer to the appropriate wxThread +TaskStorageIndex gs_tlsForWXThread = 0; + +// if it's false, some secondary thread is holding the GUI lock +static bool gs_bGuiOwnedByMainThread = true; + +// critical section which controls access to all GUI functions: any secondary +// thread (i.e. except the main one) must enter this crit section before doing +// any GUI calls +static wxCriticalSection *gs_critsectGui = NULL; + +// critical section which protects gs_nWaitingForGui variable +static wxCriticalSection *gs_critsectWaitingForGui = NULL; + +// number of threads waiting for GUI in wxMutexGuiEnter() +static size_t gs_nWaitingForGui = 0; + +// overall number of threads, needed for determining +// the sleep value of the main event loop +size_t g_numberOfThreads = 0; + + +#if wxUSE_GUI +MPCriticalRegionID gs_guiCritical = kInvalidID; +#endif + +// ============================================================================ +// MacOS implementation of thread classes +// ============================================================================ + +/* + Notes : + + The implementation is very close to the phtreads implementation, the reason for + using MPServices is the fact that these are also available under OS 9. Thus allowing + for one common API for all current builds. + + As soon as wxThreads are on a 64 bit address space, the TLS must be extended + to use two indices one for each 32 bit part as the MP implementation is limited + to longs. + + I have three implementations for mutexes : + version A based on a binary semaphore, problem - not reentrant, version B based + on a critical region, allows for reentrancy, performance implications not + yet tested, and third a plain pthreads implementation + + The same for condition internal, one implementation by Aj Lavin and the other one + copied from the thrimpl.cpp which I assume has been more broadly tested, I've just + replaced the interlock increment with the appropriate PPC calls +*/ + +// ---------------------------------------------------------------------------- +// wxCriticalSection +// ---------------------------------------------------------------------------- + +wxCriticalSection::wxCriticalSection() +{ + MPCreateCriticalRegion( (MPCriticalRegionID*) &m_critRegion ); +} + +wxCriticalSection::~wxCriticalSection() +{ + MPDeleteCriticalRegion( (MPCriticalRegionID) m_critRegion ); +} + +void wxCriticalSection::Enter() +{ + MPEnterCriticalRegion( (MPCriticalRegionID) m_critRegion, kDurationForever ); +} + +void wxCriticalSection::Leave() +{ + MPExitCriticalRegion( (MPCriticalRegionID) m_critRegion ); +} + +// ---------------------------------------------------------------------------- +// wxMutex implementation +// ---------------------------------------------------------------------------- + +#define wxUSE_MAC_SEMAPHORE_MUTEX 0 +#define wxUSE_MAC_CRITICAL_REGION_MUTEX 1 +#define wxUSE_MAC_PTHREADS_MUTEX 0 + +#if wxUSE_MAC_PTHREADS_MUTEX + +#include + + +class wxMutexInternal +{ +public: + wxMutexInternal( wxMutexType mutexType ); + ~wxMutexInternal(); + + wxMutexError Lock(); + wxMutexError TryLock(); + wxMutexError Unlock(); + + bool IsOk() const + { return m_isOk; } + +private: + pthread_mutex_t m_mutex; + bool m_isOk; + + // wxConditionInternal uses our m_mutex + friend class wxConditionInternal; +}; + +#ifdef HAVE_PTHREAD_MUTEXATTR_T +// on some systems pthread_mutexattr_settype() is not in the headers (but it is +// in the library, otherwise we wouldn't compile this code at all) +extern "C" int pthread_mutexattr_settype( pthread_mutexattr_t *, int ); +#endif + +wxMutexInternal::wxMutexInternal( wxMutexType mutexType ) +{ + int err; + switch ( mutexType ) + { + case wxMUTEX_RECURSIVE: + // support recursive locks like Win32, i.e. a thread can lock a + // mutex which it had itself already locked + // + // unfortunately initialization of recursive mutexes is non + // portable, so try several methods +#ifdef HAVE_PTHREAD_MUTEXATTR_T + { + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); + + err = pthread_mutex_init( &m_mutex, &attr ); + } +#elif defined(HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER) + // we can use this only as initializer so we have to assign it + // first to a temp var - assigning directly to m_mutex wouldn't + // even compile + { + pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + m_mutex = mutex; + } +#else // no recursive mutexes + err = EINVAL; +#endif // HAVE_PTHREAD_MUTEXATTR_T/... + break; + + default: + wxFAIL_MSG( wxT("unknown mutex type") ); + // fall through + + case wxMUTEX_DEFAULT: + err = pthread_mutex_init( &m_mutex, NULL ); + break; + } + + m_isOk = err == 0; + if ( !m_isOk ) + { + wxLogApiError( wxT("pthread_mutex_init()"), err ); + } +} + +wxMutexInternal::~wxMutexInternal() +{ + if ( m_isOk ) + { + int err = pthread_mutex_destroy( &m_mutex ); + if ( err != 0 ) + { + wxLogApiError( wxT("pthread_mutex_destroy()"), err ); + } + } +} + +wxMutexError wxMutexInternal::Lock() +{ + int err = pthread_mutex_lock( &m_mutex ); + switch ( err ) + { + case EDEADLK: + // only error checking mutexes return this value and so it's an + // unexpected situation -- hence use assert, not wxLogDebug + wxFAIL_MSG( wxT("mutex deadlock prevented") ); + return wxMUTEX_DEAD_LOCK; + + case EINVAL: + wxLogDebug( wxT("pthread_mutex_lock(): mutex not initialized.") ); + break; + + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError( wxT("pthread_mutex_lock()"), err ); + } + + return wxMUTEX_MISC_ERROR; +} + +wxMutexError wxMutexInternal::TryLock() +{ + int err = pthread_mutex_trylock( &m_mutex ); + switch ( err ) + { + case EBUSY: + // not an error: mutex is already locked, but we're prepared for this case + return wxMUTEX_BUSY; + + case EINVAL: + wxLogDebug( wxT("pthread_mutex_trylock(): mutex not initialized.") ); + break; + + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError( wxT("pthread_mutex_trylock()"), err ); + } + + return wxMUTEX_MISC_ERROR; +} + +wxMutexError wxMutexInternal::Unlock() +{ + int err = pthread_mutex_unlock( &m_mutex ); + switch ( err ) + { + case EPERM: + // we don't own the mutex + return wxMUTEX_UNLOCKED; + + case EINVAL: + wxLogDebug( wxT("pthread_mutex_unlock(): mutex not initialized.") ); + break; + + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError( wxT("pthread_mutex_unlock()"), err ); + } + + return wxMUTEX_MISC_ERROR; +} + +#endif + +#if wxUSE_MAC_SEMAPHORE_MUTEX + +class wxMutexInternal +{ +public: + wxMutexInternal( wxMutexType mutexType ); + virtual ~wxMutexInternal(); + + bool IsOk() const + { return m_isOk; } + + wxMutexError Lock(); + wxMutexError TryLock(); + wxMutexError Unlock(); + +private: + MPSemaphoreID m_semaphore; + bool m_isOk; +}; + +wxMutexInternal::wxMutexInternal(wxMutexType mutexType ) +{ + m_isOk = false; + m_semaphore = kInvalidID; + OSStatus err = noErr; + + switch ( mutexType ) + { + case wxMUTEX_DEFAULT : + verify_noerr( MPCreateBinarySemaphore( &m_semaphore ) ); + m_isOk = ( m_semaphore != kInvalidID ); + break; + + case wxMUTEX_RECURSIVE : + wxFAIL_MSG( wxT("Recursive Mutex not supported yet") ); + break; + + default : + wxFAIL_MSG( wxT("Unknown mutex type") ); + break; + } +} + +wxMutexInternal::~wxMutexInternal() +{ + if ( m_semaphore != kInvalidID ) + MPDeleteSemaphore( m_semaphore ); + + MPYield(); +} + +wxMutexError wxMutexInternal::Lock() +{ + wxCHECK_MSG( m_isOk, wxMUTEX_MISC_ERROR, wxT("Invalid Mutex") ); + OSStatus err = MPWaitOnSemaphore( m_semaphore, kDurationForever ); + if (err != noErr) + { + wxLogSysError( wxT("Could not lock mutex") ); + + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} + +wxMutexError wxMutexInternal::TryLock() +{ + wxCHECK_MSG( m_isOk, wxMUTEX_MISC_ERROR, wxT("Invalid Mutex") ); + OSStatus err = MPWaitOnSemaphore( m_semaphore, kDurationImmediate ); + if (err != noErr) + { + if (err == kMPTimeoutErr) + return wxMUTEX_BUSY; + + wxLogSysError( wxT("Could not try lock mutex") ); + + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} + +wxMutexError wxMutexInternal::Unlock() +{ + wxCHECK_MSG( m_isOk, wxMUTEX_MISC_ERROR, wxT("Invalid Mutex") ); + OSStatus err = MPSignalSemaphore( m_semaphore ); + + MPYield(); + if (err != noErr) + { + wxLogSysError( wxT("Could not unlock mutex") ); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} + +#endif + +#if wxUSE_MAC_CRITICAL_REGION_MUTEX + +class wxMutexInternal +{ +public: + wxMutexInternal( wxMutexType mutexType ); + virtual ~wxMutexInternal(); + + bool IsOk() const { return m_isOk; } + + wxMutexError Lock() { return Lock(kDurationForever); } + wxMutexError Lock(unsigned long ms); + wxMutexError TryLock(); + wxMutexError Unlock(); + +private: + MPCriticalRegionID m_critRegion; + bool m_isOk ; +}; + +wxMutexInternal::wxMutexInternal( wxMutexType WXUNUSED(mutexType) ) +{ + m_isOk = false; + m_critRegion = kInvalidID; + + verify_noerr( MPCreateCriticalRegion( &m_critRegion ) ); + m_isOk = ( m_critRegion != kInvalidID ); + if ( !IsOk() ) + { + wxFAIL_MSG( wxT("Error when creating mutex") ); + } +} + +wxMutexInternal::~wxMutexInternal() +{ + if ( m_critRegion != kInvalidID ) + MPDeleteCriticalRegion( m_critRegion ); + + MPYield(); +} + +wxMutexError wxMutexInternal::Lock(unsigned long ms) +{ + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ); + + OSStatus err = MPEnterCriticalRegion( m_critRegion, ms ); + switch ( err ) + { + case noErr: + break; + + case kMPTimeoutErr: + wxASSERT_MSG( ms != kDurationForever, wxT("unexpected timeout") ); + return wxMUTEX_TIMEOUT; + + default: + wxLogSysError(wxT("Could not lock mutex")); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} + +wxMutexError wxMutexInternal::TryLock() +{ + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + + OSStatus err = MPEnterCriticalRegion( m_critRegion, kDurationImmediate); + if (err != noErr) + { + if ( err == kMPTimeoutErr) + return wxMUTEX_BUSY; + + wxLogSysError( wxT("Could not try lock mutex") ); + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} + +wxMutexError wxMutexInternal::Unlock() +{ + wxCHECK_MSG( m_isOk , wxMUTEX_MISC_ERROR , wxT("Invalid Mutex") ) ; + + OSStatus err = MPExitCriticalRegion( m_critRegion ); + MPYield() ; + + if (err != noErr) + { + wxLogSysError( wxT("Could not unlock mutex") ); + + return wxMUTEX_MISC_ERROR; + } + + return wxMUTEX_NO_ERROR; +} + +#endif + +// -------------------------------------------------------------------------- +// wxSemaphore +// -------------------------------------------------------------------------- + +class wxSemaphoreInternal +{ +public: + wxSemaphoreInternal( int initialcount, int maxcount ); + virtual ~wxSemaphoreInternal(); + + bool IsOk() const + { return m_isOk; } + + wxSemaError Post(); + wxSemaError WaitTimeout( unsigned long milliseconds ); + + wxSemaError Wait() + { return WaitTimeout( kDurationForever); } + + wxSemaError TryWait() + { + wxSemaError err = WaitTimeout( kDurationImmediate ); + if (err == wxSEMA_TIMEOUT) + err = wxSEMA_BUSY; + + return err; + } + +private: + MPSemaphoreID m_semaphore; + bool m_isOk; +}; + +wxSemaphoreInternal::wxSemaphoreInternal( int initialcount, int maxcount) +{ + m_isOk = false; + m_semaphore = kInvalidID; + if ( maxcount == 0 ) + // make it practically infinite + maxcount = INT_MAX; + + verify_noerr( MPCreateSemaphore( maxcount, initialcount, &m_semaphore ) ); + m_isOk = ( m_semaphore != kInvalidID ); + + if ( !IsOk() ) + { + wxFAIL_MSG( wxT("Error when creating semaphore") ); + } +} + +wxSemaphoreInternal::~wxSemaphoreInternal() +{ + if (m_semaphore != kInvalidID) + MPDeleteSemaphore( m_semaphore ); + + MPYield(); +} + +wxSemaError wxSemaphoreInternal::WaitTimeout( unsigned long milliseconds ) +{ + OSStatus err = MPWaitOnSemaphore( m_semaphore, milliseconds ); + if (err != noErr) + { + if (err == kMPTimeoutErr) + return wxSEMA_TIMEOUT; + + return wxSEMA_MISC_ERROR; + } + + return wxSEMA_NO_ERROR; +} + +wxSemaError wxSemaphoreInternal::Post() +{ + OSStatus err = MPSignalSemaphore( m_semaphore ); + MPYield(); + if (err != noErr) + return wxSEMA_MISC_ERROR; + + return wxSEMA_NO_ERROR; +} + +// ---------------------------------------------------------------------------- +// wxCondition implementation +// ---------------------------------------------------------------------------- + +#if 0 + +class wxConditionInternal +{ +public: + wxConditionInternal( wxMutex& mutex ) + : + m_mutex( mutex ), + m_semaphore( 0, 1 ), + m_gate( 1, 1 ) + { + m_waiters = 0; + m_signals = 0; + m_canceled = 0; + } + + virtual ~wxConditionInternal() {} + + bool IsOk() const + { return m_mutex.IsOk(); } + + wxCondError Wait() + { return WaitTimeout( kDurationForever ); } + + wxCondError WaitTimeout( unsigned long msectimeout ); + + wxCondError Signal() + { return DoSignal( false); } + + wxCondError Broadcast() + { return DoSignal( true ); } + +private: + wxCondError DoSignal( bool signalAll ); + + wxMutex& m_mutex; + wxSemaphoreInternal m_semaphore; // Signals the waiting threads. + wxSemaphoreInternal m_gate; + wxCriticalSection m_varSection; + size_t m_waiters; // Number of threads waiting for a signal. + size_t m_signals; // Number of signals to send. + size_t m_canceled; // Number of canceled waiters in m_waiters. +}; + +wxCondError wxConditionInternal::WaitTimeout( unsigned long msectimeout ) +{ + m_gate.Wait(); + + if ( ++ m_waiters == INT_MAX ) + { + m_varSection.Enter(); + + m_waiters -= m_canceled; + m_signals -= m_canceled; + m_canceled = 0; + + m_varSection.Leave(); + } + + m_gate.Post(); + m_mutex.Unlock(); + + wxSemaError err = m_semaphore.WaitTimeout( msectimeout); + wxASSERT( err == wxSEMA_NO_ERROR || err == wxSEMA_TIMEOUT); + + m_varSection.Enter(); + + if ( err != wxSEMA_NO_ERROR ) + { + if ( m_signals > m_canceled ) + { + // A signal is being sent after we timed out. + if ( m_waiters == m_signals ) + { + // There are no excess waiters to catch the signal, so + // we must throw it away. + wxSemaError err2 = m_semaphore.Wait(); + if ( err2 != wxSEMA_NO_ERROR ) + { + wxLogSysError( wx("Error while waiting on semaphore") ); + } + + wxASSERT( err2 == wxSEMA_NO_ERROR); + + --m_waiters; + if ( --m_signals == m_canceled ) + { + // This was the last signal. open the gate. + wxASSERT( m_waiters == m_canceled ); + m_gate.Post(); + } + } + else + { + // There are excess waiters to catch the signal, leave it be. + --m_waiters; + } + } + else + { + // No signals is being sent: + // the gate may be open or closed, so we can't touch m_waiters. + ++m_canceled; + ++m_signals; + } + } + else + { + // We caught a signal. + wxASSERT( m_signals > m_canceled ); + + --m_waiters; + + if ( --m_signals == m_canceled) + { + // This was the last signal. open the gate. + wxASSERT( m_waiters == m_canceled ); + + m_gate.Post(); + } + } + + m_varSection.Leave(); + m_mutex.Lock(); + + if (err != noErr) + return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR; + + return wxCOND_NO_ERROR; +} + + +wxCondError wxConditionInternal::DoSignal( bool signalAll) +{ + m_gate.Wait(); + m_varSection.Enter(); + + wxASSERT( m_signals == m_canceled ); + + if ( m_waiters == m_canceled) + { + m_varSection.Leave(); + m_gate.Post(); + return wxCOND_NO_ERROR; + } + + if ( m_canceled > 0) + { + m_waiters -= m_canceled; + m_signals = 0; + m_canceled = 0; + } + + m_signals = signalAll ? m_waiters : 1; + size_t n = m_signals; + + m_varSection.Leave(); + + // Let the waiters inherit the gate lock. + + do + { + wxSemaError err = m_semaphore.Post(); + wxASSERT( err == wxSEMA_NO_ERROR ); + } + while ( --n ); + + return wxCOND_NO_ERROR; +} + +#else +class wxConditionInternal +{ +public: + wxConditionInternal( wxMutex& mutex ); + + bool IsOk() const + { return m_mutex.IsOk() && m_semaphore.IsOk(); } + + wxCondError Wait(); + wxCondError WaitTimeout( unsigned long milliseconds ); + + wxCondError Signal(); + wxCondError Broadcast(); + +private: + // the number of threads currently waiting for this condition + SInt32 m_numWaiters; + + // the critical section protecting m_numWaiters + wxCriticalSection m_csWaiters; + + wxMutex& m_mutex; + wxSemaphore m_semaphore; + + DECLARE_NO_COPY_CLASS(wxConditionInternal) +}; + +wxConditionInternal::wxConditionInternal( wxMutex& mutex ) + : m_mutex(mutex) +{ + // another thread can't access it until we return from ctor, so no need to + // protect access to m_numWaiters here + m_numWaiters = 0; +} + +wxCondError wxConditionInternal::Wait() +{ + // increment the number of waiters + IncrementAtomic( &m_numWaiters ); + + m_mutex.Unlock(); + + // a potential race condition can occur here + // + // after a thread increments nwaiters, and unlocks the mutex and before the + // semaphore.Wait() is called, if another thread can cause a signal to be + // generated + // + // this race condition is handled by using a semaphore and incrementing the + // semaphore only if 'nwaiters' is greater that zero since the semaphore, + // can 'remember' signals the race condition will not occur + + // wait ( if necessary ) and decrement semaphore + wxSemaError err = m_semaphore.Wait(); + m_mutex.Lock(); + + return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR; +} + +wxCondError wxConditionInternal::WaitTimeout( unsigned long milliseconds ) +{ + IncrementAtomic( &m_numWaiters ); + + m_mutex.Unlock(); + + // a race condition can occur at this point in the code + // + // please see the comments in Wait(), for details + + wxSemaError err = m_semaphore.WaitTimeout(milliseconds); + + if ( err == wxSEMA_TIMEOUT ) + { + // another potential race condition exists here it is caused when a + // 'waiting' thread timesout, and returns from WaitForSingleObject, but + // has not yet decremented 'nwaiters'. + // + // at this point if another thread calls signal() then the semaphore + // will be incremented, but the waiting thread will miss it. + // + // to handle this particular case, the waiting thread calls + // WaitForSingleObject again with a timeout of 0, after locking + // 'nwaiters_mutex'. this call does not block because of the zero + // timeout, but will allow the waiting thread to catch the missed + // signals. + wxCriticalSectionLocker lock(m_csWaiters); + + err = m_semaphore.WaitTimeout(0); + + if ( err != wxSEMA_NO_ERROR ) + { + m_numWaiters--; + } + } + + m_mutex.Lock(); + + return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR; +} + +wxCondError wxConditionInternal::Signal() +{ + wxCriticalSectionLocker lock(m_csWaiters); + + if ( m_numWaiters > 0 ) + { + // increment the semaphore by 1 + if ( m_semaphore.Post() != wxSEMA_NO_ERROR ) + return wxCOND_MISC_ERROR; + + m_numWaiters--; + } + + return wxCOND_NO_ERROR; +} + +wxCondError wxConditionInternal::Broadcast() +{ + wxCriticalSectionLocker lock(m_csWaiters); + + while ( m_numWaiters > 0 ) + { + if ( m_semaphore.Post() != wxSEMA_NO_ERROR ) + return wxCOND_MISC_ERROR; + + m_numWaiters--; + } + + return wxCOND_NO_ERROR; +} +#endif + +// ---------------------------------------------------------------------------- +// wxCriticalSection implementation +// ---------------------------------------------------------------------------- + +// XXX currently implemented as mutex in headers. Change to critical section. + +// ---------------------------------------------------------------------------- +// wxThread implementation +// ---------------------------------------------------------------------------- + +// wxThreadInternal class +// ---------------------- + +class wxThreadInternal +{ +public: + wxThreadInternal() + { + m_tid = kInvalidID; + m_state = STATE_NEW; + m_prio = WXTHREAD_DEFAULT_PRIORITY; + m_notifyQueueId = kInvalidID; + m_exitcode = 0; + m_cancelled = false ; + + // set to true only when the thread starts waiting on m_semSuspend + m_isPaused = false; + + // defaults for joinable threads + m_shouldBeJoined = true; + m_isDetached = false; + } + + virtual ~wxThreadInternal() + { + if ( m_notifyQueueId) + { + MPDeleteQueue( m_notifyQueueId ); + m_notifyQueueId = kInvalidID ; + } + } + + // thread function + static OSStatus MacThreadStart(void* arg); + + // create a new (suspended) thread (for the given thread object) + bool Create(wxThread *thread, unsigned int stackSize); + + // thread actions + + // start the thread + wxThreadError Run(); + + // unblock the thread allowing it to run + void SignalRun() { m_semRun.Post(); } + + // ask the thread to terminate + void Wait(); + + // go to sleep until Resume() is called + void Pause(); + + // resume the thread + void Resume(); + + // accessors + // priority + int GetPriority() const + { return m_prio; } + void SetPriority(int prio); + + // state + wxThreadState GetState() const + { return m_state; } + void SetState(wxThreadState state) + { m_state = state; } + + // Get the ID of this thread's underlying MP Services task. + MPTaskID GetId() const + { return m_tid; } + + void SetCancelFlag() + { m_cancelled = true; } + + bool WasCancelled() const + { return m_cancelled; } + + // exit code + void SetExitCode(wxThread::ExitCode exitcode) + { m_exitcode = exitcode; } + wxThread::ExitCode GetExitCode() const + { return m_exitcode; } + + // the pause flag + void SetReallyPaused(bool paused) + { m_isPaused = paused; } + bool IsReallyPaused() const + { return m_isPaused; } + + // tell the thread that it is a detached one + void Detach() + { + wxCriticalSectionLocker lock(m_csJoinFlag); + + m_shouldBeJoined = false; + m_isDetached = true; + } + +private: + // the thread we're associated with + wxThread * m_thread; + + MPTaskID m_tid; // thread id + MPQueueID m_notifyQueueId; // its notification queue + + wxThreadState m_state; // see wxThreadState enum + int m_prio; // in wxWidgets units: from 0 to 100 + + // this flag is set when the thread should terminate + bool m_cancelled; + + // this flag is set when the thread is blocking on m_semSuspend + bool m_isPaused; + + // the thread exit code - only used for joinable (!detached) threads and + // is only valid after the thread termination + wxThread::ExitCode m_exitcode; + + // many threads may call Wait(), but only one of them should call + // pthread_join(), so we have to keep track of this + wxCriticalSection m_csJoinFlag; + bool m_shouldBeJoined; + bool m_isDetached; + + // this semaphore is posted by Run() and the threads Entry() is not + // called before it is done + wxSemaphore m_semRun; + + // this one is signaled when the thread should resume after having been + // Pause()d + wxSemaphore m_semSuspend; +}; + +OSStatus wxThreadInternal::MacThreadStart(void *parameter) +{ + wxThread* thread = (wxThread*) parameter ; + wxThreadInternal *pthread = thread->m_internal; + + // add to TLS so that This() will work + verify_noerr( MPSetTaskStorageValue( gs_tlsForWXThread , (TaskStorageValue) thread ) ) ; + + // have to declare this before pthread_cleanup_push() which defines a + // block! + bool dontRunAtAll; + + // wait for the semaphore to be posted from Run() + pthread->m_semRun.Wait(); + + // test whether we should run the run at all - may be it was deleted + // before it started to Run()? + { + wxCriticalSectionLocker lock(thread->m_critsect); + + dontRunAtAll = pthread->GetState() == STATE_NEW && + pthread->WasCancelled(); + } + + if ( !dontRunAtAll ) + { + pthread->m_exitcode = thread->Entry(); + + { + wxCriticalSectionLocker lock(thread->m_critsect); + pthread->SetState( STATE_EXITED ); + } + } + + if ( dontRunAtAll ) + { + if ( pthread->m_isDetached ) + delete thread; + + return -1; + } + else + { + // on Mac for the running code, + // the correct thread termination is to return + + // terminate the thread + thread->Exit( pthread->m_exitcode ); + + return (OSStatus) NULL; // pthread->m_exitcode; + } +} + +bool wxThreadInternal::Create( wxThread *thread, unsigned int stackSize ) +{ + wxASSERT_MSG( m_state == STATE_NEW && !m_tid, + wxT("Create()ing thread twice?") ); + + OSStatus err = noErr; + m_thread = thread; + + if ( m_notifyQueueId == kInvalidID ) + { + OSStatus err = MPCreateQueue( &m_notifyQueueId ); + if (err != noErr) + { + wxLogSysError( wxT("Cant create the thread event queue") ); + + return false; + } + } + + m_state = STATE_NEW; + + err = MPCreateTask( + MacThreadStart, (void*)m_thread, stackSize, + m_notifyQueueId, &m_exitcode, 0, 0, &m_tid ); + + if (err != noErr) + { + wxLogSysError( wxT("Can't create thread") ); + + return false; + } + + if ( m_prio != WXTHREAD_DEFAULT_PRIORITY ) + SetPriority( m_prio ); + + return true; +} + +void wxThreadInternal::SetPriority( int priority ) +{ + m_prio = priority; + + if (m_tid) + { + // Mac priorities range from 1 to 10,000, with a default of 100. + // wxWidgets priorities range from 0 to 100 with a default of 50. + // We can map wxWidgets to Mac priorities easily by assuming + // the former uses a logarithmic scale. + const unsigned int macPriority = (int)( exp( priority / 25.0 * log( 10.0)) + 0.5); + + MPSetTaskWeight( m_tid, macPriority ); + } +} + +wxThreadError wxThreadInternal::Run() +{ + wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING, + wxT("thread may only be started once after Create()") ); + + SetState( STATE_RUNNING ); + + // wake up threads waiting for our start + SignalRun(); + + return wxTHREAD_NO_ERROR; +} + +void wxThreadInternal::Wait() +{ + wxCHECK_RET( !m_isDetached, wxT("can't wait for a detached thread") ); + + // if the thread we're waiting for is waiting for the GUI mutex, we will + // deadlock so make sure we release it temporarily + if ( wxThread::IsMain() ) + { + // give the thread we're waiting for chance to do the GUI call + // it might be in, we don't do this conditionally as the to be waited on + // thread might have to acquire the mutex later but before terminating + if ( wxGuiOwnedByMainThread() ) + wxMutexGuiLeave(); + } + + { + wxCriticalSectionLocker lock(m_csJoinFlag); + + if ( m_shouldBeJoined ) + { + void *param1, *param2, *rc; + + OSStatus err = MPWaitOnQueue( + m_notifyQueueId, + ¶m1, + ¶m2, + &rc, + kDurationForever ); + if (err != noErr) + { + wxLogSysError( wxT( "Cannot wait for thread termination.")); + rc = (void*) -1; + } + + // actually param1 would be the address of m_exitcode + // but we don't need this here + m_exitcode = rc; + + m_shouldBeJoined = false; + } + } +} + +void wxThreadInternal::Pause() +{ + // the state is set from the thread which pauses us first, this function + // is called later so the state should have been already set + wxCHECK_RET( m_state == STATE_PAUSED, + wxT("thread must first be paused with wxThread::Pause().") ); + + // wait until the semaphore is Post()ed from Resume() + m_semSuspend.Wait(); +} + +void wxThreadInternal::Resume() +{ + wxCHECK_RET( m_state == STATE_PAUSED, + wxT("can't resume thread which is not suspended.") ); + + // the thread might be not actually paused yet - if there were no call to + // TestDestroy() since the last call to Pause() for example + if ( IsReallyPaused() ) + { + // wake up Pause() + m_semSuspend.Post(); + + // reset the flag + SetReallyPaused( false ); + } + + SetState( STATE_RUNNING ); +} + +// static functions +// ---------------- + +wxThread *wxThread::This() +{ + wxThread* thr = (wxThread*) MPGetTaskStorageValue( gs_tlsForWXThread ) ; + + return thr; +} + +bool wxThread::IsMain() +{ + return GetCurrentId() == gs_idMainThread || gs_idMainThread == kInvalidID ; +} + +#ifdef Yield +#undef Yield +#endif + +void wxThread::Yield() +{ + CFRunLoopRunInMode( kCFRunLoopDefaultMode , 0 , true ) ; + + MPYield(); +} + +void wxThread::Sleep( unsigned long milliseconds ) +{ + AbsoluteTime wakeup = AddDurationToAbsolute( milliseconds, UpTime() ); + MPDelayUntil( &wakeup ); +} + +int wxThread::GetCPUCount() +{ + return MPProcessors(); +} + +unsigned long wxThread::GetCurrentId() +{ + return (unsigned long)MPCurrentTaskID(); +} + +bool wxThread::SetConcurrency( size_t WXUNUSED(level) ) +{ + // Cannot be set in MacOS. + return false; +} + +wxThread::wxThread( wxThreadKind kind ) +{ + g_numberOfThreads++; + m_internal = new wxThreadInternal(); + + m_isDetached = (kind == wxTHREAD_DETACHED); +} + +wxThread::~wxThread() +{ + wxASSERT_MSG( g_numberOfThreads>0 , wxT("More threads deleted than created.") ) ; + + g_numberOfThreads--; + +#ifdef __WXDEBUG__ + m_critsect.Enter(); + + // check that the thread either exited or couldn't be created + if ( m_internal->GetState() != STATE_EXITED && + m_internal->GetState() != STATE_NEW ) + { + wxLogDebug( + wxT("The thread %ld is being destroyed although it is still running! The application may crash."), + GetId() ); + } + + m_critsect.Leave(); +#endif + + wxDELETE( m_internal ) ; +} + +wxThreadError wxThread::Create( unsigned int stackSize ) +{ + wxCriticalSectionLocker lock(m_critsect); + + if ( m_isDetached ) + m_internal->Detach() ; + + if ( !m_internal->Create(this, stackSize) ) + { + m_internal->SetState( STATE_EXITED ); + + return wxTHREAD_NO_RESOURCE; + } + + return wxTHREAD_NO_ERROR; +} + +wxThreadError wxThread::Run() +{ + wxCriticalSectionLocker lock(m_critsect); + + wxCHECK_MSG( m_internal->GetId(), wxTHREAD_MISC_ERROR, + wxT("must call wxThread::Create() first") ); + + return m_internal->Run(); +} + +// ----------------------------------------------------------------------------- +// pause/resume +// ----------------------------------------------------------------------------- + +wxThreadError wxThread::Pause() +{ + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + _T("a thread can't pause itself") ); + + wxCriticalSectionLocker lock(m_critsect); + + if ( m_internal->GetState() != STATE_RUNNING ) + { + wxLogDebug( wxT("Can't pause thread which is not running.") ); + + return wxTHREAD_NOT_RUNNING; + } + + // just set a flag, the thread will be really paused only during the next + // call to TestDestroy() + m_internal->SetState( STATE_PAUSED ); + + return wxTHREAD_NO_ERROR; +} + +wxThreadError wxThread::Resume() +{ + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + wxT("a thread can't resume itself") ); + + wxCriticalSectionLocker lock(m_critsect); + + wxThreadState state = m_internal->GetState(); + + switch ( state ) + { + case STATE_PAUSED: + m_internal->Resume(); + return wxTHREAD_NO_ERROR; + + case STATE_EXITED: + return wxTHREAD_NO_ERROR; + + default: + wxLogDebug( wxT("Attempt to resume a thread which is not paused.") ); + + return wxTHREAD_MISC_ERROR; + } +} + +// ----------------------------------------------------------------------------- +// exiting thread +// ----------------------------------------------------------------------------- + +wxThread::ExitCode wxThread::Wait() +{ + wxCHECK_MSG( This() != this, (ExitCode)-1, + wxT("a thread can't wait for itself") ); + + wxCHECK_MSG( !m_isDetached, (ExitCode)-1, + wxT("can't wait for detached thread") ); + + m_internal->Wait(); + + return m_internal->GetExitCode(); +} + +wxThreadError wxThread::Delete(ExitCode *rc) +{ + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + wxT("a thread can't delete itself") ); + + bool isDetached = m_isDetached; + + m_critsect.Enter(); + wxThreadState state = m_internal->GetState(); + + // ask the thread to stop + m_internal->SetCancelFlag(); + + m_critsect.Leave(); + + switch ( state ) + { + case STATE_NEW: + // we need to wake up the thread so that PthreadStart() will + // terminate - right now it's blocking on run semaphore in + // PthreadStart() + m_internal->SignalRun(); + + // fall through + + case STATE_EXITED: + // nothing to do + break; + + case STATE_PAUSED: + // resume the thread first + m_internal->Resume(); + + // fall through + + default: + if ( !isDetached ) + { + // wait until the thread stops + m_internal->Wait(); + + if ( rc ) + { + // return the exit code of the thread + *rc = m_internal->GetExitCode(); + } + } + } + + return wxTHREAD_NO_ERROR; +} + +wxThreadError wxThread::Kill() +{ + wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR, + wxT("a thread can't kill itself") ); + + switch ( m_internal->GetState() ) + { + case STATE_NEW: + case STATE_EXITED: + return wxTHREAD_NOT_RUNNING; + + case STATE_PAUSED: + // resume the thread first + Resume(); + + // fall through + + default: + OSStatus err = MPTerminateTask( m_internal->GetId() , -1 ) ; + if (err != noErr) + { + wxLogError( wxT("Failed to terminate a thread.") ); + + return wxTHREAD_MISC_ERROR; + } + + if ( m_isDetached ) + { + delete this ; + } + else + { + // this should be retrieved by Wait actually + m_internal->SetExitCode( (void*)-1 ); + } + + return wxTHREAD_NO_ERROR; + } +} + +void wxThread::Exit( ExitCode status ) +{ + wxASSERT_MSG( This() == this, + wxT("wxThread::Exit() can only be called in the context of the same thread") ); + + // don't enter m_critsect before calling OnExit() because the user code + // might deadlock if, for example, it signals a condition in OnExit() (a + // common case) while the main thread calls any of functions entering + // m_critsect on us (almost all of them do) + OnExit(); + + MPTaskID threadid = m_internal->GetId(); + + if ( IsDetached() ) + { + delete this; + } + else // joinable + { + // update the status of the joinable thread + wxCriticalSectionLocker lock( m_critsect ); + m_internal->SetState( STATE_EXITED ); + } + + MPTerminateTask( threadid, (long)status ); +} + +// also test whether we were paused +bool wxThread::TestDestroy() +{ + wxASSERT_MSG( This() == this, + wxT("wxThread::TestDestroy() can only be called in the context of the same thread") ); + + m_critsect.Enter(); + + if ( m_internal->GetState() == STATE_PAUSED ) + { + m_internal->SetReallyPaused( true ); + + // leave the crit section or the other threads will stop too if they attempt + // to call any of (seemingly harmless) IsXXX() functions while we sleep + m_critsect.Leave(); + + m_internal->Pause(); + } + else + { + // thread wasn't requested to pause, nothing to do + m_critsect.Leave(); + } + + return m_internal->WasCancelled(); +} + +// ----------------------------------------------------------------------------- +// priority setting +// ----------------------------------------------------------------------------- + +void wxThread::SetPriority(unsigned int prio) +{ + wxCHECK_RET( ((int)WXTHREAD_MIN_PRIORITY <= (int)prio) && + ((int)prio <= (int)WXTHREAD_MAX_PRIORITY), + wxT("invalid thread priority") ); + + wxCriticalSectionLocker lock(m_critsect); + + switch ( m_internal->GetState() ) + { + case STATE_RUNNING: + case STATE_PAUSED: + case STATE_NEW: + // thread not yet started, priority will be set when it is + m_internal->SetPriority( prio ); + break; + + case STATE_EXITED: + default: + wxFAIL_MSG( wxT("impossible to set thread priority in this state") ); + } +} + +unsigned int wxThread::GetPriority() const +{ + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast + + return m_internal->GetPriority(); +} + +unsigned long wxThread::GetId() const +{ + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast + + return (unsigned long)m_internal->GetId(); +} + +// ----------------------------------------------------------------------------- +// state tests +// ----------------------------------------------------------------------------- + +bool wxThread::IsRunning() const +{ + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + + return m_internal->GetState() == STATE_RUNNING; +} + +bool wxThread::IsAlive() const +{ + wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect); + + switch ( m_internal->GetState() ) + { + case STATE_RUNNING: + case STATE_PAUSED: + return true; + + default: + return false; + } +} + +bool wxThread::IsPaused() const +{ + wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect); + + return (m_internal->GetState() == STATE_PAUSED); +} + +// ---------------------------------------------------------------------------- +// Automatic initialization for thread module +// ---------------------------------------------------------------------------- + +class wxThreadModule : public wxModule +{ +public: + virtual bool OnInit(); + virtual void OnExit(); + +private: + DECLARE_DYNAMIC_CLASS(wxThreadModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule) + +bool wxThreadModule::OnInit() +{ + bool hasThreadManager = +#ifdef __LP64__ + true ; // TODO VERIFY IN NEXT BUILD +#else + MPLibraryIsLoaded(); +#endif + + if ( !hasThreadManager ) + { + wxLogError( wxT("MP thread support is not available on this system" ) ) ; + + return false; + } + + // main thread's This() is NULL + verify_noerr( MPAllocateTaskStorageIndex( &gs_tlsForWXThread ) ) ; + verify_noerr( MPSetTaskStorageValue( gs_tlsForWXThread, 0 ) ) ; + + gs_idMainThread = wxThread::GetCurrentId(); + gs_critsectWaitingForGui = new wxCriticalSection(); + + gs_critsectGui = new wxCriticalSection(); + gs_critsectGui->Enter(); + + return true; +} + +void wxThreadModule::OnExit() +{ + if ( gs_critsectGui ) + { + if ( !wxGuiOwnedByMainThread() ) + { + gs_critsectGui->Enter(); + gs_bGuiOwnedByMainThread = true; + } + + gs_critsectGui->Leave(); + delete gs_critsectGui; + gs_critsectGui = NULL; + } + + delete gs_critsectWaitingForGui; + gs_critsectWaitingForGui = NULL; +} + +// ---------------------------------------------------------------------------- +// GUI Serialization copied from MSW implementation +// ---------------------------------------------------------------------------- + +void wxMutexGuiEnterImpl() +{ + // this would dead lock everything... + wxASSERT_MSG( !wxThread::IsMain(), + wxT("main thread doesn't want to block in wxMutexGuiEnter()!") ); + + // the order in which we enter the critical sections here is crucial!! + + // set the flag telling to the main thread that we want to do some GUI + { + wxCriticalSectionLocker enter(*gs_critsectWaitingForGui); + + gs_nWaitingForGui++; + } + + wxWakeUpMainThread(); + + // now we may block here because the main thread will soon let us in + // (during the next iteration of OnIdle()) + gs_critsectGui->Enter(); +} + +void wxMutexGuiLeaveImpl() +{ + wxCriticalSectionLocker enter(*gs_critsectWaitingForGui); + + if ( wxThread::IsMain() ) + { + gs_bGuiOwnedByMainThread = false; + } + else + { + // decrement the number of threads waiting for GUI access now + wxASSERT_MSG( gs_nWaitingForGui > 0, + wxT("calling wxMutexGuiLeave() without entering it first?") ); + + gs_nWaitingForGui--; + + wxWakeUpMainThread(); + } + + gs_critsectGui->Leave(); +} + +void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter() +{ + wxASSERT_MSG( wxThread::IsMain(), + wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") ); + + if ( !gs_critsectWaitingForGui ) + return; + + wxCriticalSectionLocker enter(*gs_critsectWaitingForGui); + + if ( gs_nWaitingForGui == 0 ) + { + // no threads are waiting for GUI - so we may acquire the lock without + // any danger (but only if we don't already have it) + if ( !wxGuiOwnedByMainThread() ) + { + gs_critsectGui->Enter(); + + gs_bGuiOwnedByMainThread = true; + } + //else: already have it, nothing to do + } + else + { + // some threads are waiting, release the GUI lock if we have it + if ( wxGuiOwnedByMainThread() ) + wxMutexGuiLeave(); + //else: some other worker thread is doing GUI + } +} + +bool WXDLLIMPEXP_BASE wxGuiOwnedByMainThread() +{ + return gs_bGuiOwnedByMainThread; +} + +// wake up the main thread +void WXDLLEXPORT wxWakeUpMainThread() +{ + wxMacWakeUp(); +} + +// ---------------------------------------------------------------------------- +// include common implementation code +// ---------------------------------------------------------------------------- + +#include "wx/thrimpl.cpp" + +#endif // wxUSE_THREADS diff --git a/src/osx/carbon/timer.cpp b/src/osx/carbon/timer.cpp new file mode 100644 index 0000000000..c5beb16cf1 --- /dev/null +++ b/src/osx/carbon/timer.cpp @@ -0,0 +1,99 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: sec/mac/carbon/timer.cpp +// Purpose: wxTimer implementation +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_TIMER + +#ifndef WX_PRECOMP + #include "wx/dynarray.h" +#endif + +#include "wx/mac/private/timer.h" + +#ifdef __WXMAC__ + #include "wx/mac/private.h" +#endif + +struct MacTimerInfo +{ + wxCarbonTimerImpl* m_timer; + EventLoopTimerUPP m_proc; + EventLoopTimerRef m_timerRef; +}; + +static pascal void wxProcessTimer( EventLoopTimerRef WXUNUSED(theTimer), void *data ) +{ + if ( data == NULL ) + return; + + wxCarbonTimerImpl* timer = (wxCarbonTimerImpl*)data; + + if ( timer->IsOneShot() ) + timer->Stop(); + + timer->Notify(); +} + +wxCarbonTimerImpl::wxCarbonTimerImpl(wxTimer *timer) + : wxTimerImpl(timer) +{ + m_info = new MacTimerInfo(); + m_info->m_timer = this; + m_info->m_proc = NULL; + m_info->m_timerRef = kInvalidID; +} + +bool wxCarbonTimerImpl::IsRunning() const +{ + return ( m_info->m_timerRef != kInvalidID ); +} + +wxCarbonTimerImpl::~wxCarbonTimerImpl() +{ + delete m_info; +} + +bool wxCarbonTimerImpl::Start( int milliseconds, bool mode ) +{ + (void)wxTimerImpl::Start(milliseconds, mode); + + wxCHECK_MSG( m_milli > 0, false, wxT("invalid value for timer timeout") ); + wxCHECK_MSG( m_info->m_timerRef == NULL, false, wxT("attempting to restart a timer") ); + + m_info->m_timer = this; + m_info->m_proc = NewEventLoopTimerUPP( &wxProcessTimer ); + + OSStatus err = InstallEventLoopTimer( + GetMainEventLoop(), + m_milli*kEventDurationMillisecond, + IsOneShot() ? 0 : m_milli * kEventDurationMillisecond, + m_info->m_proc, + this, + &m_info->m_timerRef ); + verify_noerr( err ); + + return true; +} + +void wxCarbonTimerImpl::Stop() +{ + if (m_info->m_timerRef) + RemoveEventLoopTimer( m_info->m_timerRef ); + if (m_info->m_proc) + DisposeEventLoopTimerUPP( m_info->m_proc ); + + m_info->m_proc = NULL; + m_info->m_timerRef = kInvalidID; +} + +#endif // wxUSE_TIMER + diff --git a/src/osx/carbon/toolbar.cpp b/src/osx/carbon/toolbar.cpp new file mode 100644 index 0000000000..f0364473c9 --- /dev/null +++ b/src/osx/carbon/toolbar.cpp @@ -0,0 +1,1735 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/toolbar.cpp +// Purpose: wxToolBar +// Author: Stefan Csomor +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_TOOLBAR + +#include "wx/toolbar.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "wx/app.h" +#include "wx/mac/uma.h" +#include "wx/geometry.h" +#include "wx/sysopt.h" + + +const short kwxMacToolBarToolDefaultWidth = 16; +const short kwxMacToolBarToolDefaultHeight = 16; +const short kwxMacToolBarTopMargin = 4; +const short kwxMacToolBarLeftMargin = 4; +const short kwxMacToolBorder = 0; +const short kwxMacToolSpacing = 6; + + +IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl) + +BEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase) + EVT_PAINT( wxToolBar::OnPaint ) +END_EVENT_TABLE() + + +#pragma mark - +#pragma mark Tool Implementation + + +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- + +// We have a dual implementation for each tool, ControlRef and HIToolbarItemRef + +// when embedding native controls in the native toolbar we must make sure the +// control does not get deleted behind our backs, so the retain count gets increased +// (after creation it is 1), first be the creation of the custom HIToolbarItem wrapper +// object, and second by the code 'creating' the custom HIView (which is the same as the +// already existing native control, therefore we just increase the ref count) +// when this view is removed from the native toolbar its count gets decremented again +// and when the HITooolbarItem wrapper object gets destroyed it is decremented as well +// so in the end the control lives with a refcount of one and can be disposed of by the +// wxControl code. For embedded controls on a non-native toolbar this ref count is less +// so we can only test against a range, not a specific value of the refcount. + +class wxToolBarTool : public wxToolBarToolBase +{ +public: + wxToolBarTool( + wxToolBar *tbar, + int id, + const wxString& label, + const wxBitmap& bmpNormal, + const wxBitmap& bmpDisabled, + wxItemKind kind, + wxObject *clientData, + const wxString& shortHelp, + const wxString& longHelp ); + + wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label) + : wxToolBarToolBase(tbar, control, label) + { + Init(); + if (control != NULL) + SetControlHandle( (ControlRef) control->GetHandle() ); + } + + virtual ~wxToolBarTool() + { + ClearControl(); + } + + WXWidget GetControlHandle() + { + return (WXWidget) m_controlHandle; + } + + void SetControlHandle( ControlRef handle ) + { + m_controlHandle = handle; + } + + void SetPosition( const wxPoint& position ); + + void ClearControl() + { + if ( m_controlHandle ) + { + if ( !IsControl() ) + DisposeControl( m_controlHandle ); + else + { + // the embedded control is not under the responsibility of the tool, it gets disposed of in the + // proper wxControl destructor + } + m_controlHandle = NULL ; + } + +#if wxMAC_USE_NATIVE_TOOLBAR + if ( m_toolbarItemRef ) + { + CFIndex count = CFGetRetainCount( m_toolbarItemRef ) ; + // different behaviour under Leopard + if ( UMAGetSystemVersion() < 0x1050 ) + { + if ( count != 1 ) + { + wxFAIL_MSG("Reference count of native tool was not 1 in wxToolBarTool destructor"); + } + } + wxTheApp->MacAddToAutorelease(m_toolbarItemRef); + CFRelease(m_toolbarItemRef); + m_toolbarItemRef = NULL; + } +#endif // wxMAC_USE_NATIVE_TOOLBAR + } + + wxSize GetSize() const + { + wxSize curSize; + + if ( IsControl() ) + { + curSize = GetControl()->GetSize(); + } + else if ( IsButton() ) + { + curSize = GetToolBar()->GetToolSize(); + } + else + { + // separator size + curSize = GetToolBar()->GetToolSize(); + if ( GetToolBar()->GetWindowStyleFlag() & wxTB_VERTICAL ) + curSize.y /= 4; + else + curSize.x /= 4; + } + + return curSize; + } + + wxPoint GetPosition() const + { + return wxPoint( m_x, m_y ); + } + + bool DoEnable( bool enable ); + + void UpdateToggleImage( bool toggle ); + +#if wxMAC_USE_NATIVE_TOOLBAR + void SetToolbarItemRef( HIToolbarItemRef ref ) + { + if ( m_controlHandle ) + HideControl( m_controlHandle ); + if ( m_toolbarItemRef ) + CFRelease( m_toolbarItemRef ); + + m_toolbarItemRef = ref; + if ( m_toolbarItemRef ) + { + wxFont f; + wxFontEncoding enc; + if ( GetToolBar() ) + f = GetToolBar()->GetFont(); + if ( f.IsOk() ) + enc = f.GetEncoding(); + else + enc = wxFont::GetDefaultEncoding(); + + HIToolbarItemSetHelpText( + m_toolbarItemRef, + wxCFStringRef( GetShortHelp(), enc ), + wxCFStringRef( GetLongHelp(), enc ) ); + } + } + + HIToolbarItemRef GetToolbarItemRef() const + { + return m_toolbarItemRef; + } + + void SetIndex( CFIndex idx ) + { + m_index = idx; + } + + CFIndex GetIndex() const + { + return m_index; + } +#endif + +private: + void Init() + { + m_controlHandle = NULL; + +#if wxMAC_USE_NATIVE_TOOLBAR + m_toolbarItemRef = NULL; + m_index = -1; +#endif + } + + ControlRef m_controlHandle; + wxCoord m_x; + wxCoord m_y; + +#if wxMAC_USE_NATIVE_TOOLBAR + HIToolbarItemRef m_toolbarItemRef; + // position in its toolbar, -1 means not inserted + CFIndex m_index; +#endif +}; + +static const EventTypeSpec eventList[] = +{ + { kEventClassControl, kEventControlHit }, + { kEventClassControl, kEventControlHitTest }, +}; + +static pascal OSStatus wxMacToolBarToolControlEventHandler( EventHandlerCallRef WXUNUSED(handler), EventRef event, void *data ) +{ + OSStatus result = eventNotHandledErr; + ControlRef controlRef; + wxMacCarbonEvent cEvent( event ); + + cEvent.GetParameter( kEventParamDirectObject, &controlRef ); + + switch ( GetEventKind( event ) ) + { + case kEventControlHit: + { + wxToolBarTool *tbartool = (wxToolBarTool*)data; + wxToolBar *tbar = tbartool != NULL ? (wxToolBar*) (tbartool->GetToolBar()) : NULL; + if ((tbartool != NULL) && tbartool->CanBeToggled()) + { + bool shouldToggle; + + shouldToggle = !tbartool->IsToggled(); + + tbar->ToggleTool( tbartool->GetId(), shouldToggle ); + } + + if (tbartool != NULL) + tbar->OnLeftClick( tbartool->GetId(), tbartool->IsToggled() ); + result = noErr; + } + break; + + case kEventControlHitTest: + { + HIPoint pt = cEvent.GetParameter(kEventParamMouseLocation); + HIRect rect; + HIViewGetBounds( controlRef, &rect ); + + ControlPartCode pc = kControlNoPart; + if ( CGRectContainsPoint( rect, pt ) ) + pc = kControlIconPart; + cEvent.SetParameter( kEventParamControlPart, typeControlPartCode, pc ); + result = noErr; + } + break; + + default: + break; + } + + return result; +} + +static pascal OSStatus wxMacToolBarToolEventHandler( EventHandlerCallRef handler, EventRef event, void *data ) +{ + OSStatus result = eventNotHandledErr; + + switch ( GetEventClass( event ) ) + { + case kEventClassControl: + result = wxMacToolBarToolControlEventHandler( handler, event, data ); + break; + + default: + break; + } + + return result; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacToolBarToolEventHandler ) + +#if wxMAC_USE_NATIVE_TOOLBAR + +static const EventTypeSpec toolBarEventList[] = +{ + { kEventClassToolbarItem, kEventToolbarItemPerformAction }, +}; + +static pascal OSStatus wxMacToolBarCommandEventHandler( EventHandlerCallRef WXUNUSED(handler), EventRef event, void *data ) +{ + OSStatus result = eventNotHandledErr; + + switch ( GetEventKind( event ) ) + { + case kEventToolbarItemPerformAction: + { + wxToolBarTool* tbartool = (wxToolBarTool*) data; + if ( tbartool != NULL ) + { + wxToolBar *tbar = (wxToolBar*)(tbartool->GetToolBar()); + int toolID = tbartool->GetId(); + + if ( tbartool->CanBeToggled() ) + { + if ( tbar != NULL ) + tbar->ToggleTool(toolID, !tbartool->IsToggled() ); + } + + if ( tbar != NULL ) + tbar->OnLeftClick( toolID, tbartool->IsToggled() ); + result = noErr; + } + } + break; + + default: + break; + } + + return result; +} + +static pascal OSStatus wxMacToolBarEventHandler( EventHandlerCallRef handler, EventRef event, void *data ) +{ + OSStatus result = eventNotHandledErr; + + switch ( GetEventClass( event ) ) + { + case kEventClassToolbarItem: + result = wxMacToolBarCommandEventHandler( handler, event, data ); + break; + + default: + break; + } + + return result; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacToolBarEventHandler ) + +#endif + +bool wxToolBarTool::DoEnable( bool enable ) +{ + if ( IsControl() ) + { + GetControl()->Enable( enable ); + } + else if ( IsButton() ) + { +#if wxMAC_USE_NATIVE_TOOLBAR + if ( m_toolbarItemRef != NULL ) + HIToolbarItemSetEnabled( m_toolbarItemRef, enable ); +#endif + + if ( m_controlHandle != NULL ) + { + if ( enable ) + EnableControl( m_controlHandle ); + else + DisableControl( m_controlHandle ); + } + } + + return true; +} + +void wxToolBarTool::SetPosition( const wxPoint& position ) +{ + m_x = position.x; + m_y = position.y; + + int mac_x = position.x; + int mac_y = position.y; + + if ( IsButton() ) + { + Rect contrlRect; + GetControlBounds( m_controlHandle, &contrlRect ); + int former_mac_x = contrlRect.left; + int former_mac_y = contrlRect.top; + GetToolBar()->GetToolSize(); + + if ( mac_x != former_mac_x || mac_y != former_mac_y ) + { + ::MoveControl( m_controlHandle, mac_x, mac_y ); + } + } + else if ( IsControl() ) + { + // embedded native controls are moved by the OS +#if wxMAC_USE_NATIVE_TOOLBAR + if ( ((wxToolBar*)GetToolBar())->MacWantsNativeToolbar() == false ) +#endif + { + GetControl()->Move( position ); + } + } + else + { + // separator + Rect contrlRect; + GetControlBounds( m_controlHandle, &contrlRect ); + int former_mac_x = contrlRect.left; + int former_mac_y = contrlRect.top; + + if ( mac_x != former_mac_x || mac_y != former_mac_y ) + ::MoveControl( m_controlHandle, mac_x, mac_y ); + } +} + +void wxToolBarTool::UpdateToggleImage( bool toggle ) +{ + if ( toggle ) + { + int w = m_bmpNormal.GetWidth(); + int h = m_bmpNormal.GetHeight(); + wxBitmap bmp( w, h ); + wxMemoryDC dc; + + dc.SelectObject( bmp ); + dc.SetPen( wxPen(*wxBLACK) ); + dc.SetBrush( wxBrush( *wxLIGHT_GREY )); + dc.DrawRectangle( 0, 0, w, h ); + dc.DrawBitmap( m_bmpNormal, 0, 0, true ); + dc.SelectObject( wxNullBitmap ); + ControlButtonContentInfo info; + wxMacCreateBitmapButton( &info, bmp ); + SetControlData( m_controlHandle, 0, kControlIconContentTag, sizeof(info), (Ptr)&info ); +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_toolbarItemRef != NULL) + { + ControlButtonContentInfo info2; + wxMacCreateBitmapButton( &info2, bmp, kControlContentCGImageRef); + HIToolbarItemSetImage( m_toolbarItemRef, info2.u.imageRef ); + wxMacReleaseBitmapButton( &info2 ); + } +#endif + wxMacReleaseBitmapButton( &info ); + } + else + { + ControlButtonContentInfo info; + wxMacCreateBitmapButton( &info, m_bmpNormal ); + SetControlData( m_controlHandle, 0, kControlIconContentTag, sizeof(info), (Ptr)&info ); +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_toolbarItemRef != NULL) + { + ControlButtonContentInfo info2; + wxMacCreateBitmapButton( &info2, m_bmpNormal, kControlContentCGImageRef); + HIToolbarItemSetImage( m_toolbarItemRef, info2.u.imageRef ); + wxMacReleaseBitmapButton( &info2 ); + } +#endif + wxMacReleaseBitmapButton( &info ); + } + + IconTransformType transform = toggle ? kTransformSelected : kTransformNone; + SetControlData( + m_controlHandle, 0, kControlIconTransformTag, + sizeof(transform), (Ptr)&transform ); + HIViewSetNeedsDisplay( m_controlHandle, true ); + +} + +wxToolBarTool::wxToolBarTool( + wxToolBar *tbar, + int id, + const wxString& label, + const wxBitmap& bmpNormal, + const wxBitmap& bmpDisabled, + wxItemKind kind, + wxObject *clientData, + const wxString& shortHelp, + const wxString& longHelp ) + : + wxToolBarToolBase( + tbar, id, label, bmpNormal, bmpDisabled, kind, + clientData, shortHelp, longHelp ) +{ + Init(); +} + +#pragma mark - +#pragma mark Toolbar Implementation + +wxToolBarToolBase *wxToolBar::CreateTool( + int id, + const wxString& label, + const wxBitmap& bmpNormal, + const wxBitmap& bmpDisabled, + wxItemKind kind, + wxObject *clientData, + const wxString& shortHelp, + const wxString& longHelp ) +{ + return new wxToolBarTool( + this, id, label, bmpNormal, bmpDisabled, kind, + clientData, shortHelp, longHelp ); +} + +wxToolBarToolBase * +wxToolBar::CreateTool(wxControl *control, const wxString& label) +{ + return new wxToolBarTool(this, control, label); +} + +void wxToolBar::Init() +{ + m_maxWidth = -1; + m_maxHeight = -1; + m_defaultWidth = kwxMacToolBarToolDefaultWidth; + m_defaultHeight = kwxMacToolBarToolDefaultHeight; + +#if wxMAC_USE_NATIVE_TOOLBAR + m_macHIToolbarRef = NULL; + m_macUsesNativeToolbar = false; +#endif +} + +#define kControlToolbarItemClassID CFSTR( "org.wxwidgets.controltoolbaritem" ) + +const EventTypeSpec kEvents[] = +{ + { kEventClassHIObject, kEventHIObjectConstruct }, + { kEventClassHIObject, kEventHIObjectInitialize }, + { kEventClassHIObject, kEventHIObjectDestruct }, + + { kEventClassToolbarItem, kEventToolbarItemCreateCustomView } +}; + +const EventTypeSpec kViewEvents[] = +{ + { kEventClassControl, kEventControlGetSizeConstraints } +}; + +struct ControlToolbarItem +{ + HIToolbarItemRef toolbarItem; + HIViewRef viewRef; + wxSize lastValidSize ; +}; + +static pascal OSStatus ControlToolbarItemHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ) +{ + OSStatus result = eventNotHandledErr; + ControlToolbarItem* object = (ControlToolbarItem*)inUserData; + + switch ( GetEventClass( inEvent ) ) + { + case kEventClassHIObject: + switch ( GetEventKind( inEvent ) ) + { + case kEventHIObjectConstruct: + { + HIObjectRef toolbarItem; + ControlToolbarItem* item; + + GetEventParameter( inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, + sizeof( HIObjectRef ), NULL, &toolbarItem ); + + item = (ControlToolbarItem*) malloc(sizeof(ControlToolbarItem)) ; + item->toolbarItem = toolbarItem ; + item->lastValidSize = wxSize(-1,-1); + item->viewRef = NULL ; + + SetEventParameter( inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof( void * ), &item ); + + result = noErr ; + } + break; + + case kEventHIObjectInitialize: + result = CallNextEventHandler( inCallRef, inEvent ); + if ( result == noErr ) + { + CFDataRef data; + GetEventParameter( inEvent, kEventParamToolbarItemConfigData, typeCFTypeRef, NULL, + sizeof( CFTypeRef ), NULL, &data ); + + HIViewRef viewRef ; + + wxASSERT_MSG( CFDataGetLength( data ) == sizeof( viewRef ) , wxT("Illegal Data passed") ) ; + memcpy( &viewRef , CFDataGetBytePtr( data ) , sizeof( viewRef ) ) ; + + object->viewRef = (HIViewRef) viewRef ; + // make sure we keep that control during our lifetime + CFRetain( object->viewRef ) ; + + verify_noerr(InstallEventHandler( GetControlEventTarget( viewRef ), ControlToolbarItemHandler, + GetEventTypeCount( kViewEvents ), kViewEvents, object, NULL )); + result = noErr ; + } + break; + + case kEventHIObjectDestruct: + { + HIViewRef viewRef = object->viewRef ; + if( viewRef && IsValidControlHandle( viewRef) ) + { + // depending whether the wxControl corresponding to this HIView has already been destroyed or + // not, ref counts differ, so we cannot assert a special value + CFIndex count = CFGetRetainCount( viewRef ) ; + if ( count >= 1 ) + { + wxFAIL_MSG("Reference count of native tool was illegal before removal"); + + CFRelease( viewRef ) ; + } + } + free( object ) ; + result = noErr; + } + break; + } + break; + + case kEventClassToolbarItem: + switch ( GetEventKind( inEvent ) ) + { + case kEventToolbarItemCreateCustomView: + { + HIViewRef viewRef = object->viewRef ; + HIViewRemoveFromSuperview( viewRef ) ; + HIViewSetVisible(viewRef, true) ; + CFRetain( viewRef ) ; + result = SetEventParameter( inEvent, kEventParamControlRef, typeControlRef, sizeof( HIViewRef ), &viewRef ); + } + break; + } + break; + + case kEventClassControl: + switch ( GetEventKind( inEvent ) ) + { + case kEventControlGetSizeConstraints: + { + wxWindow* wxwindow = wxFindControlFromMacControl(object->viewRef ) ; + if ( wxwindow ) + { + // during toolbar layout the native window sometimes gets negative sizes, + // sometimes it just gets shrunk behind our back, so in order to avoid + // ever shrinking more, once a valid size is captured, we keep it + + wxSize sz = object->lastValidSize; + if ( sz.x <= 0 || sz.y <= 0 ) + { + sz = wxwindow->GetSize() ; + sz.x -= wxwindow->MacGetLeftBorderSize() + wxwindow->MacGetRightBorderSize(); + sz.y -= wxwindow->MacGetTopBorderSize() + wxwindow->MacGetBottomBorderSize(); + if ( sz.x > 0 && sz.y > 0 ) + object->lastValidSize = sz ; + else + sz = wxSize(0,0) ; + } + + // Extra width to avoid edge of combobox being cut off + sz.x += 3; + + HISize min, max; + min.width = max.width = sz.x ; + min.height = max.height = sz.y ; + + result = SetEventParameter( inEvent, kEventParamMinimumSize, typeHISize, + sizeof( HISize ), &min ); + + result = SetEventParameter( inEvent, kEventParamMaximumSize, typeHISize, + sizeof( HISize ), &max ); + result = noErr ; + } + } + break; + } + break; + } + + return result; +} + +void RegisterControlToolbarItemClass() +{ + static bool sRegistered; + + if ( !sRegistered ) + { + HIObjectRegisterSubclass( kControlToolbarItemClassID, kHIToolbarItemClassID, 0, + ControlToolbarItemHandler, GetEventTypeCount( kEvents ), kEvents, 0, NULL ); + + sRegistered = true; + } +} + +HIToolbarItemRef CreateControlToolbarItem(CFStringRef inIdentifier, CFTypeRef inConfigData) +{ + RegisterControlToolbarItemClass(); + + OSStatus err; + EventRef event; + UInt32 options = kHIToolbarItemAllowDuplicates; + HIToolbarItemRef result = NULL; + + err = CreateEvent( NULL, kEventClassHIObject, kEventHIObjectInitialize, GetCurrentEventTime(), 0, &event ); + require_noerr( err, CantCreateEvent ); + + SetEventParameter( event, kEventParamAttributes, typeUInt32, sizeof( UInt32 ), &options ); + SetEventParameter( event, kEventParamToolbarItemIdentifier, typeCFStringRef, sizeof( CFStringRef ), &inIdentifier ); + + if ( inConfigData ) + SetEventParameter( event, kEventParamToolbarItemConfigData, typeCFTypeRef, sizeof( CFTypeRef ), &inConfigData ); + + err = HIObjectCreate( kControlToolbarItemClassID, event, (HIObjectRef*)&result ); + check_noerr( err ); + + ReleaseEvent( event ); +CantCreateEvent : + return result ; +} + +#if wxMAC_USE_NATIVE_TOOLBAR +static const EventTypeSpec kToolbarEvents[] = +{ + { kEventClassToolbar, kEventToolbarGetDefaultIdentifiers }, + { kEventClassToolbar, kEventToolbarGetAllowedIdentifiers }, + { kEventClassToolbar, kEventToolbarCreateItemWithIdentifier }, +}; + +static OSStatus ToolbarDelegateHandler(EventHandlerCallRef WXUNUSED(inCallRef), + EventRef inEvent, + void* WXUNUSED(inUserData)) +{ + OSStatus result = eventNotHandledErr; + // Not yet needed + // wxToolBar* toolbar = (wxToolBar*) inUserData ; + CFMutableArrayRef array; + + switch ( GetEventKind( inEvent ) ) + { + case kEventToolbarGetDefaultIdentifiers: + { + GetEventParameter( inEvent, kEventParamMutableArray, typeCFMutableArrayRef, NULL, + sizeof( CFMutableArrayRef ), NULL, &array ); + // not implemented yet + // GetToolbarDefaultItems( array ); + result = noErr; + } + break; + + case kEventToolbarGetAllowedIdentifiers: + { + GetEventParameter( inEvent, kEventParamMutableArray, typeCFMutableArrayRef, NULL, + sizeof( CFMutableArrayRef ), NULL, &array ); + // not implemented yet + // GetToolbarAllowedItems( array ); + result = noErr; + } + break; + case kEventToolbarCreateItemWithIdentifier: + { + HIToolbarItemRef item = NULL; + CFTypeRef data = NULL; + CFStringRef identifier = NULL ; + + GetEventParameter( inEvent, kEventParamToolbarItemIdentifier, typeCFStringRef, NULL, + sizeof( CFStringRef ), NULL, &identifier ); + + GetEventParameter( inEvent, kEventParamToolbarItemConfigData, typeCFTypeRef, NULL, + sizeof( CFTypeRef ), NULL, &data ); + + if ( CFStringCompare( kControlToolbarItemClassID, identifier, kCFCompareBackwards ) == kCFCompareEqualTo ) + { + item = CreateControlToolbarItem( kControlToolbarItemClassID, data ); + if ( item ) + { + SetEventParameter( inEvent, kEventParamToolbarItem, typeHIToolbarItemRef, + sizeof( HIToolbarItemRef ), &item ); + result = noErr; + } + } + + } + break; + } + return result ; +} +#endif // wxMAC_USE_NATIVE_TOOLBAR + +// also for the toolbar we have the dual implementation: +// only when MacInstallNativeToolbar is called is the native toolbar set as the window toolbar + +bool wxToolBar::Create( + wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name ) +{ + if ( !wxToolBarBase::Create( parent, id, pos, size, style, wxDefaultValidator, name ) ) + return false; + + FixupStyle(); + + OSStatus err = noErr; + +#if wxMAC_USE_NATIVE_TOOLBAR + if (parent->IsKindOf(CLASSINFO(wxFrame)) && wxSystemOptions::GetOptionInt(wxT("mac.toolbar.no-native")) != 1) + { + wxString labelStr = wxString::Format( wxT("%p"), this ); + err = HIToolbarCreate( + wxCFStringRef( labelStr, wxFont::GetDefaultEncoding() ), 0, + (HIToolbarRef*) &m_macHIToolbarRef ); + + if (m_macHIToolbarRef != NULL) + { + InstallEventHandler( HIObjectGetEventTarget((HIToolbarRef)m_macHIToolbarRef ), ToolbarDelegateHandler, + GetEventTypeCount( kToolbarEvents ), kToolbarEvents, this, NULL ); + + HIToolbarDisplayMode mode = kHIToolbarDisplayModeDefault; + HIToolbarDisplaySize displaySize = kHIToolbarDisplaySizeSmall; + + if ( style & wxTB_NOICONS ) + mode = kHIToolbarDisplayModeLabelOnly; + else if ( style & wxTB_TEXT ) + mode = kHIToolbarDisplayModeIconAndLabel; + else + mode = kHIToolbarDisplayModeIconOnly; + + HIToolbarSetDisplayMode( (HIToolbarRef) m_macHIToolbarRef, mode ); + HIToolbarSetDisplaySize( (HIToolbarRef) m_macHIToolbarRef, displaySize ); + } + } +#endif // wxMAC_USE_NATIVE_TOOLBAR + + return (err == noErr); +} + +wxToolBar::~wxToolBar() +{ +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + // if this is the installed toolbar, then deinstall it + if (m_macUsesNativeToolbar) + MacInstallNativeToolbar( false ); + + CFIndex count = CFGetRetainCount( m_macHIToolbarRef ) ; + // Leopard seems to have one refcount more, so we cannot check reliably at the moment + if ( UMAGetSystemVersion() < 0x1050 ) + { + if ( count != 1 ) + { + wxFAIL_MSG("Reference count of native control was not 1 in wxToolBar destructor"); + } + } + CFRelease( (HIToolbarRef)m_macHIToolbarRef ); + m_macHIToolbarRef = NULL; + } +#endif +} + +bool wxToolBar::Show( bool show ) +{ + WindowRef tlw = MAC_WXHWND(MacGetTopLevelWindowRef()); + bool bResult = (tlw != NULL); + + if (bResult) + { +#if wxMAC_USE_NATIVE_TOOLBAR + bool ownToolbarInstalled = false; + MacTopLevelHasNativeToolbar( &ownToolbarInstalled ); + if (ownToolbarInstalled) + { + bResult = (IsWindowToolbarVisible( tlw ) != show); + if ( bResult ) + ShowHideWindowToolbar( tlw, show, false ); + } + else + bResult = wxToolBarBase::Show( show ); +#else + + bResult = wxToolBarBase::Show( show ); +#endif + } + + return bResult; +} + +bool wxToolBar::IsShown() const +{ + bool bResult; + +#if wxMAC_USE_NATIVE_TOOLBAR + bool ownToolbarInstalled; + + MacTopLevelHasNativeToolbar( &ownToolbarInstalled ); + if (ownToolbarInstalled) + { + WindowRef tlw = MAC_WXHWND(MacGetTopLevelWindowRef()); + bResult = IsWindowToolbarVisible( tlw ); + } + else + bResult = wxToolBarBase::IsShown(); +#else + + bResult = wxToolBarBase::IsShown(); +#endif + + return bResult; +} + +void wxToolBar::DoGetSize( int *width, int *height ) const +{ +#if wxMAC_USE_NATIVE_TOOLBAR + Rect boundsR; + bool ownToolbarInstalled; + + MacTopLevelHasNativeToolbar( &ownToolbarInstalled ); + if ( ownToolbarInstalled ) + { + // TODO: is this really a control ? + GetControlBounds( (ControlRef) m_macHIToolbarRef, &boundsR ); + if ( width != NULL ) + *width = boundsR.right - boundsR.left; + if ( height != NULL ) + *height = boundsR.bottom - boundsR.top; + } + else + wxToolBarBase::DoGetSize( width, height ); + +#else + wxToolBarBase::DoGetSize( width, height ); +#endif +} + +wxSize wxToolBar::DoGetBestSize() const +{ + int width, height; + + DoGetSize( &width, &height ); + + return wxSize( width, height ); +} + +void wxToolBar::SetWindowStyleFlag( long style ) +{ + wxToolBarBase::SetWindowStyleFlag( style ); + +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + HIToolbarDisplayMode mode = kHIToolbarDisplayModeDefault; + + if ( style & wxTB_NOICONS ) + mode = kHIToolbarDisplayModeLabelOnly; + else if ( style & wxTB_TEXT ) + mode = kHIToolbarDisplayModeIconAndLabel; + else + mode = kHIToolbarDisplayModeIconOnly; + + HIToolbarSetDisplayMode( (HIToolbarRef) m_macHIToolbarRef, mode ); + } +#endif +} + +#if wxMAC_USE_NATIVE_TOOLBAR +bool wxToolBar::MacWantsNativeToolbar() +{ + return m_macUsesNativeToolbar; +} + +bool wxToolBar::MacTopLevelHasNativeToolbar(bool *ownToolbarInstalled) const +{ + bool bResultV = false; + + if (ownToolbarInstalled != NULL) + *ownToolbarInstalled = false; + + WindowRef tlw = MAC_WXHWND(MacGetTopLevelWindowRef()); + if (tlw != NULL) + { + HIToolbarRef curToolbarRef = NULL; + OSStatus err = GetWindowToolbar( tlw, &curToolbarRef ); + bResultV = ((err == noErr) && (curToolbarRef != NULL)); + if (bResultV && (ownToolbarInstalled != NULL)) + *ownToolbarInstalled = (curToolbarRef == m_macHIToolbarRef); + } + + return bResultV; +} + +bool wxToolBar::MacInstallNativeToolbar(bool usesNative) +{ + bool bResult = false; + + if (usesNative && (m_macHIToolbarRef == NULL)) + return bResult; + + if (usesNative && ((GetWindowStyleFlag() & wxTB_VERTICAL) != 0)) + return bResult; + + WindowRef tlw = MAC_WXHWND(MacGetTopLevelWindowRef()); + if (tlw == NULL) + return bResult; + + // check the existing toolbar + HIToolbarRef curToolbarRef = NULL; + OSStatus err = GetWindowToolbar( tlw, &curToolbarRef ); + if (err != noErr) + curToolbarRef = NULL; + + m_macUsesNativeToolbar = usesNative; + + if (m_macUsesNativeToolbar) + { + // only install toolbar if there isn't one installed already + if (curToolbarRef == NULL) + { + bResult = true; + + SetWindowToolbar( tlw, (HIToolbarRef) m_macHIToolbarRef ); + ShowHideWindowToolbar( tlw, true, false ); + ChangeWindowAttributes( tlw, kWindowToolbarButtonAttribute, 0 ); + SetAutomaticControlDragTrackingEnabledForWindow( tlw, true ); + + Rect r = { 0, 0, 0, 0 }; + m_peer->SetRect( &r ); + SetSize( wxSIZE_AUTO_WIDTH, 0 ); + m_peer->SetVisibility( false, true ); + wxToolBarBase::Show( false ); + } + } + else + { + // only deinstall toolbar if this is the installed one + if (m_macHIToolbarRef == curToolbarRef) + { + bResult = true; + + ShowHideWindowToolbar( tlw, false, false ); + ChangeWindowAttributes( tlw, 0, kWindowToolbarButtonAttribute ); + SetWindowToolbar( tlw, NULL ); + + m_peer->SetVisibility( true, true ); + } + } + + if (bResult) + InvalidateBestSize(); + +// wxLogDebug( wxT(" --> [%lx] - result [%s]"), (long)this, bResult ? wxT("T") : wxT("F") ); + return bResult; +} +#endif + +bool wxToolBar::Realize() +{ + if (m_tools.GetCount() == 0) + return false; + + int maxWidth = 0; + int maxHeight = 0; + + int maxToolWidth = 0; + int maxToolHeight = 0; + + int x = m_xMargin + kwxMacToolBarLeftMargin; + int y = m_yMargin + kwxMacToolBarTopMargin; + + int tw, th; + GetSize( &tw, &th ); + + // find the maximum tool width and height + wxToolBarTool *tool; + wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst(); + while ( node ) + { + tool = (wxToolBarTool *) node->GetData(); + if ( tool != NULL ) + { + wxSize sz = tool->GetSize(); + + if ( sz.x > maxToolWidth ) + maxToolWidth = sz.x; + if ( sz.y > maxToolHeight ) + maxToolHeight = sz.y; + } + + node = node->GetNext(); + } + + bool lastIsRadio = false; + bool curIsRadio = false; + +#if wxMAC_USE_NATIVE_TOOLBAR + CFIndex currentPosition = 0; + bool insertAll = false; + + HIToolbarRef refTB = (HIToolbarRef)m_macHIToolbarRef; +#endif + + node = m_tools.GetFirst(); + while ( node ) + { + tool = (wxToolBarTool*) node->GetData(); + if ( tool == NULL ) + { + node = node->GetNext(); + continue; + } + + // set tool position: + // for the moment just perform a single row/column alignment + wxSize cursize = tool->GetSize(); + if ( x + cursize.x > maxWidth ) + maxWidth = x + cursize.x; + if ( y + cursize.y > maxHeight ) + maxHeight = y + cursize.y; + + if ( GetWindowStyleFlag() & wxTB_VERTICAL ) + { + int x1 = x + ( maxToolWidth - cursize.x ) / 2; + tool->SetPosition( wxPoint(x1, y) ); + } + else + { + int y1 = y + ( maxToolHeight - cursize.y ) / 2; + tool->SetPosition( wxPoint(x, y1) ); + } + + // update the item positioning state + if ( GetWindowStyleFlag() & wxTB_VERTICAL ) + y += cursize.y + kwxMacToolSpacing; + else + x += cursize.x + kwxMacToolSpacing; + +#if wxMAC_USE_NATIVE_TOOLBAR + // install in native HIToolbar + if ( refTB ) + { + HIToolbarItemRef hiItemRef = tool->GetToolbarItemRef(); + if ( hiItemRef != NULL ) + { + if ( insertAll || (tool->GetIndex() != currentPosition) ) + { + OSStatus err = noErr; + if ( !insertAll ) + { + insertAll = true; + + // if this is the first tool that gets newly inserted or repositioned + // first remove all 'old' tools from here to the right, because of this + // all following tools will have to be reinserted (insertAll). + for ( wxToolBarToolsList::compatibility_iterator node2 = m_tools.GetLast(); + node2 != node; + node2 = node2->GetPrevious() ) + { + wxToolBarTool *tool2 = (wxToolBarTool*) node2->GetData(); + + const long idx = tool2->GetIndex(); + if ( idx != -1 ) + { + if ( tool2->IsControl() ) + { + CFIndex count = CFGetRetainCount( tool2->GetControl()->GetPeer()->GetControlRef() ) ; + if ( count != 3 && count != 2 ) + { + wxFAIL_MSG("Reference count of native tool was illegal before removal"); + } + + wxASSERT( IsValidControlHandle(tool2->GetControl()->GetPeer()->GetControlRef() )) ; + } + err = HIToolbarRemoveItemAtIndex(refTB, idx); + if ( err != noErr ) + { + wxLogDebug(wxT("HIToolbarRemoveItemAtIndex(%ld) failed [%ld]"), + idx, (long)err); + } + if ( tool2->IsControl() ) + { + CFIndex count = CFGetRetainCount( tool2->GetControl()->GetPeer()->GetControlRef() ) ; + if ( count != 2 ) + { + wxFAIL_MSG("Reference count of native tool was not 2 after removal"); + } + + wxASSERT( IsValidControlHandle(tool2->GetControl()->GetPeer()->GetControlRef() )) ; + } + + tool2->SetIndex(-1); + } + } + } + + err = HIToolbarInsertItemAtIndex( refTB, hiItemRef, currentPosition ); + if (err != noErr) + { + wxLogDebug( wxT("HIToolbarInsertItemAtIndex failed [%ld]"), (long)err ); + } + + tool->SetIndex( currentPosition ); + if ( tool->IsControl() ) + { + CFIndex count = CFGetRetainCount( tool->GetControl()->GetPeer()->GetControlRef() ) ; + if ( count != 3 && count != 2 ) + { + wxFAIL_MSG("Reference count of native tool was illegal before removal"); + } + wxASSERT( IsValidControlHandle(tool->GetControl()->GetPeer()->GetControlRef() )) ; + } + } + + currentPosition++; + } + } +#endif + + // update radio button (and group) state + lastIsRadio = curIsRadio; + curIsRadio = ( tool->IsButton() && (tool->GetKind() == wxITEM_RADIO) ); + + if ( !curIsRadio ) + { + if ( tool->IsToggled() ) + DoToggleTool( tool, true ); + } + else + { + if ( !lastIsRadio ) + { + if ( tool->Toggle( true ) ) + { + DoToggleTool( tool, true ); + } + } + else if ( tool->IsToggled() ) + { + if ( tool->IsToggled() ) + DoToggleTool( tool, true ); + + wxToolBarToolsList::compatibility_iterator nodePrev = node->GetPrevious(); + while ( nodePrev ) + { + wxToolBarToolBase *toggleTool = nodePrev->GetData(); + if ( (toggleTool == NULL) || !toggleTool->IsButton() || (toggleTool->GetKind() != wxITEM_RADIO) ) + break; + + if ( toggleTool->Toggle( false ) ) + DoToggleTool( toggleTool, false ); + + nodePrev = nodePrev->GetPrevious(); + } + } + } + + node = node->GetNext(); + } + + if ( GetWindowStyleFlag() & wxTB_HORIZONTAL ) + { + // if not set yet, only one row + if ( m_maxRows <= 0 ) + SetRows( 1 ); + + m_minWidth = maxWidth; + maxWidth = tw; + maxHeight += m_yMargin + kwxMacToolBarTopMargin; + m_minHeight = m_maxHeight = maxHeight; + } + else + { + // if not set yet, have one column + if ( (GetToolsCount() > 0) && (m_maxRows <= 0) ) + SetRows( GetToolsCount() ); + + m_minHeight = maxHeight; + maxHeight = th; + maxWidth += m_xMargin + kwxMacToolBarLeftMargin; + m_minWidth = m_maxWidth = maxWidth; + } + +#if 0 + // FIXME: should this be OSX-only? + { + bool wantNativeToolbar, ownToolbarInstalled; + + // attempt to install the native toolbar + wantNativeToolbar = ((GetWindowStyleFlag() & wxTB_VERTICAL) == 0); + MacInstallNativeToolbar( wantNativeToolbar ); + (void)MacTopLevelHasNativeToolbar( &ownToolbarInstalled ); + if (!ownToolbarInstalled) + { + SetSize( maxWidth, maxHeight ); + InvalidateBestSize(); + } + } +#else + SetSize( maxWidth, maxHeight ); + InvalidateBestSize(); +#endif + + SetInitialSize(); + + return true; +} + +void wxToolBar::SetToolBitmapSize(const wxSize& size) +{ + m_defaultWidth = size.x + kwxMacToolBorder; + m_defaultHeight = size.y + kwxMacToolBorder; + +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + int maxs = wxMax( size.x, size.y ); + HIToolbarDisplaySize sizeSpec; + if ( maxs > 32 ) + sizeSpec = kHIToolbarDisplaySizeNormal; + else if ( maxs > 24 ) + sizeSpec = kHIToolbarDisplaySizeDefault; + else + sizeSpec = kHIToolbarDisplaySizeSmall; + + HIToolbarSetDisplaySize( (HIToolbarRef) m_macHIToolbarRef, sizeSpec ); + } +#endif +} + +// The button size is bigger than the bitmap size +wxSize wxToolBar::GetToolSize() const +{ + return wxSize(m_defaultWidth + kwxMacToolBorder, m_defaultHeight + kwxMacToolBorder); +} + +void wxToolBar::SetRows(int nRows) +{ + // avoid resizing the frame uselessly + if ( nRows != m_maxRows ) + m_maxRows = nRows; +} + +void wxToolBar::MacSuperChangedPosition() +{ + wxWindow::MacSuperChangedPosition(); + +#if wxMAC_USE_NATIVE_TOOLBAR + if (! m_macUsesNativeToolbar ) + Realize(); +#else + + Realize(); +#endif +} + +void wxToolBar::SetToolNormalBitmap( int id, const wxBitmap& bitmap ) +{ + wxToolBarTool* tool = wx_static_cast(wxToolBarTool*, FindById(id)); + if ( tool ) + { + wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools.")); + + tool->SetNormalBitmap(bitmap); + + // a side-effect of the UpdateToggleImage function is that it always changes the bitmap used on the button. + tool->UpdateToggleImage( tool->CanBeToggled() && tool->IsToggled() ); + } +} + +void wxToolBar::SetToolDisabledBitmap( int id, const wxBitmap& bitmap ) +{ + wxToolBarTool* tool = wx_static_cast(wxToolBarTool*, FindById(id)); + if ( tool ) + { + wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools.")); + + tool->SetDisabledBitmap(bitmap); + + // TODO: what to do for this one? + } +} + +wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const +{ + wxToolBarTool *tool; + wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst(); + while ( node ) + { + tool = (wxToolBarTool *)node->GetData(); + if (tool != NULL) + { + wxRect2DInt r( tool->GetPosition(), tool->GetSize() ); + if ( r.Contains( wxPoint( x, y ) ) ) + return tool; + } + + node = node->GetNext(); + } + + return (wxToolBarToolBase*)NULL; +} + +wxString wxToolBar::MacGetToolTipString( wxPoint &pt ) +{ + wxToolBarToolBase *tool = FindToolForPosition( pt.x, pt.y ); + if ( tool != NULL ) + return tool->GetShortHelp(); + + return wxEmptyString; +} + +void wxToolBar::DoEnableTool(wxToolBarToolBase *t, bool enable) +{ + if ( t != NULL ) + ((wxToolBarTool*)t)->DoEnable( enable ); +} + +void wxToolBar::DoToggleTool(wxToolBarToolBase *t, bool toggle) +{ + wxToolBarTool *tool = (wxToolBarTool *)t; + if ( ( tool != NULL ) && tool->IsButton() ) + tool->UpdateToggleImage( toggle ); +} + +bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos), wxToolBarToolBase *toolBase) +{ + wxToolBarTool *tool = wx_static_cast( wxToolBarTool*, toolBase ); + if (tool == NULL) + return false; + + WindowRef window = (WindowRef) MacGetTopLevelWindowRef(); + wxSize toolSize = GetToolSize(); + Rect toolrect = { 0, 0, toolSize.y, toolSize.x }; + ControlRef controlHandle = NULL; + OSStatus err = 0; + +#if wxMAC_USE_NATIVE_TOOLBAR + wxString label = tool->GetLabel(); + if (m_macHIToolbarRef && !label.empty() ) + { + // strip mnemonics from the label for compatibility + // with the usual labels in wxStaticText sense + label = wxStripMenuCodes(label); + } +#endif // wxMAC_USE_NATIVE_TOOLBAR + + switch (tool->GetStyle()) + { + case wxTOOL_STYLE_SEPARATOR: + { + wxASSERT( tool->GetControlHandle() == NULL ); + toolSize.x /= 4; + toolSize.y /= 4; + if ( GetWindowStyleFlag() & wxTB_VERTICAL ) + toolrect.bottom = toolSize.y; + else + toolrect.right = toolSize.x; + + // in flat style we need a visual separator +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + HIToolbarItemRef item; + err = HIToolbarItemCreate( + kHIToolbarSeparatorIdentifier, + kHIToolbarItemCantBeRemoved | kHIToolbarItemIsSeparator | kHIToolbarItemAllowDuplicates, + &item ); + if (err == noErr) + tool->SetToolbarItemRef( item ); + } + else + err = noErr; +#endif // wxMAC_USE_NATIVE_TOOLBAR + + CreateSeparatorControl( window, &toolrect, &controlHandle ); + tool->SetControlHandle( controlHandle ); + } + break; + + case wxTOOL_STYLE_BUTTON: + { + wxASSERT( tool->GetControlHandle() == NULL ); + ControlButtonContentInfo info; + wxMacCreateBitmapButton( &info, tool->GetNormalBitmap() ); + + if ( UMAGetSystemVersion() >= 0x1000) + { + // contrary to the docs this control only works with iconrefs + ControlButtonContentInfo info; + wxMacCreateBitmapButton( &info, tool->GetNormalBitmap(), kControlContentIconRef ); + CreateIconControl( window, &toolrect, &info, false, &controlHandle ); + wxMacReleaseBitmapButton( &info ); + } + else + { + SInt16 behaviour = kControlBehaviorOffsetContents; + if ( tool->CanBeToggled() ) + behaviour |= kControlBehaviorToggles; + err = CreateBevelButtonControl( window, + &toolrect, CFSTR(""), kControlBevelButtonNormalBevel, + behaviour, &info, 0, 0, 0, &controlHandle ); + } + +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + HIToolbarItemRef item; + wxString labelStr = wxString::Format(wxT("%p"), tool); + err = HIToolbarItemCreate( + wxCFStringRef(labelStr, wxFont::GetDefaultEncoding()), + kHIToolbarItemCantBeRemoved | kHIToolbarItemAnchoredLeft | kHIToolbarItemAllowDuplicates, &item ); + if (err == noErr) + { + ControlButtonContentInfo info2; + wxMacCreateBitmapButton( &info2, tool->GetNormalBitmap(), kControlContentCGImageRef); + + InstallEventHandler( + HIObjectGetEventTarget(item), GetwxMacToolBarEventHandlerUPP(), + GetEventTypeCount(toolBarEventList), toolBarEventList, tool, NULL ); + HIToolbarItemSetLabel( item, wxCFStringRef(label, GetFont().GetEncoding()) ); + HIToolbarItemSetImage( item, info2.u.imageRef ); + HIToolbarItemSetCommandID( item, kHIToolbarCommandPressAction ); + tool->SetToolbarItemRef( item ); + + wxMacReleaseBitmapButton( &info2 ); + } + } + else + err = noErr; +#endif // wxMAC_USE_NATIVE_TOOLBAR + + wxMacReleaseBitmapButton( &info ); + +#if 0 + SetBevelButtonTextPlacement( m_controlHandle, kControlBevelButtonPlaceBelowGraphic ); + SetControlTitleWithCFString( m_controlHandle , wxCFStringRef( label, wxFont::GetDefaultEncoding() ); +#endif + + InstallControlEventHandler( + (ControlRef) controlHandle, GetwxMacToolBarToolEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, tool, NULL ); + + tool->SetControlHandle( controlHandle ); + } + break; + + case wxTOOL_STYLE_CONTROL: + +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + wxCHECK_MSG( tool->GetControl(), false, _T("control must be non-NULL") ); + HIToolbarItemRef item; + HIViewRef viewRef = (HIViewRef) tool->GetControl()->GetHandle() ; + CFDataRef data = CFDataCreate( kCFAllocatorDefault , (UInt8*) &viewRef , sizeof(viewRef) ) ; + err = HIToolbarCreateItemWithIdentifier((HIToolbarRef) m_macHIToolbarRef,kControlToolbarItemClassID, + data , &item ) ; + + if (err == noErr) + { + tool->SetToolbarItemRef( item ); + } + CFRelease( data ) ; + } + else + { + err = noErr; + break; + } +#else + // right now there's nothing to do here +#endif + break; + + default: + break; + } + + if ( err == noErr ) + { + if ( controlHandle ) + { + ControlRef container = (ControlRef) GetHandle(); + wxASSERT_MSG( container != NULL, wxT("No valid Mac container control") ); + + SetControlVisibility( controlHandle, true, true ); + ::EmbedControl( controlHandle, container ); + } + + if ( tool->CanBeToggled() && tool->IsToggled() ) + tool->UpdateToggleImage( true ); + + // nothing special to do here - we relayout in Realize() later + InvalidateBestSize(); + } + else + { + wxFAIL_MSG( wxString::Format( wxT("wxToolBar::DoInsertTool - failure [%ld]"), (long)err ) ); + } + + return (err == noErr); +} + +void wxToolBar::DoSetToggle(wxToolBarToolBase *WXUNUSED(tool), bool WXUNUSED(toggle)) +{ + wxFAIL_MSG( wxT("not implemented") ); +} + +bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos), wxToolBarToolBase *toolbase) +{ + wxToolBarTool* tool = wx_static_cast( wxToolBarTool*, toolbase ); + wxToolBarToolsList::compatibility_iterator node; + for ( node = m_tools.GetFirst(); node; node = node->GetNext() ) + { + wxToolBarToolBase *tool2 = node->GetData(); + if ( tool2 == tool ) + { + // let node point to the next node in the list + node = node->GetNext(); + + break; + } + } + + wxSize sz = ((wxToolBarTool*)tool)->GetSize(); + +#if wxMAC_USE_NATIVE_TOOLBAR + CFIndex removeIndex = tool->GetIndex(); +#endif + +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + if ( removeIndex != -1 && m_macHIToolbarRef ) + { + HIToolbarRemoveItemAtIndex( (HIToolbarRef) m_macHIToolbarRef, removeIndex ); + tool->SetIndex( -1 ); + } + } +#endif + + tool->ClearControl(); + + // and finally reposition all the controls after this one + + for ( /* node -> first after deleted */; node; node = node->GetNext() ) + { + wxToolBarTool *tool2 = (wxToolBarTool*) node->GetData(); + wxPoint pt = tool2->GetPosition(); + + if ( GetWindowStyleFlag() & wxTB_VERTICAL ) + pt.y -= sz.y; + else + pt.x -= sz.x; + + tool2->SetPosition( pt ); + +#if wxMAC_USE_NATIVE_TOOLBAR + if (m_macHIToolbarRef != NULL) + { + if ( removeIndex != -1 && tool2->GetIndex() > removeIndex ) + tool2->SetIndex( tool2->GetIndex() - 1 ); + } +#endif + } + + InvalidateBestSize(); + + return true; +} + +void wxToolBar::OnPaint(wxPaintEvent& event) +{ +#if wxMAC_USE_NATIVE_TOOLBAR + if ( m_macUsesNativeToolbar ) + { + event.Skip(true); + return; + } +#endif + + wxPaintDC dc(this); + + int w, h; + GetSize( &w, &h ); + + bool drawMetalTheme = MacGetTopLevelWindow()->MacGetMetalAppearance(); + + if ( !drawMetalTheme ) + { + HIThemePlacardDrawInfo info; + memset( &info, 0, sizeof(info) ); + info.version = 0; + info.state = IsEnabled() ? kThemeStateActive : kThemeStateInactive; + + CGContextRef cgContext = (CGContextRef) MacGetCGContextRef(); + HIRect rect = CGRectMake( 0, 0, w, h ); + HIThemeDrawPlacard( &rect, &info, cgContext, kHIThemeOrientationNormal ); + } + else + { + // leave the background as it is (striped or metal) + } + + event.Skip(); +} + +#endif // wxUSE_TOOLBAR diff --git a/src/osx/carbon/tooltip.cpp b/src/osx/carbon/tooltip.cpp new file mode 100644 index 0000000000..957feb0491 --- /dev/null +++ b/src/osx/carbon/tooltip.cpp @@ -0,0 +1,263 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/tooltip.cpp +// Purpose: wxToolTip implementation +// Author: Stefan Csomor +// Id: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#if wxUSE_TOOLTIPS + +#include "wx/tooltip.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/window.h" + #include "wx/dc.h" + #include "wx/timer.h" +#endif // WX_PRECOMP + +#include "wx/geometry.h" +#include "wx/mac/uma.h" + +//----------------------------------------------------------------------------- +// global data +//----------------------------------------------------------------------------- + +#if wxUSE_TIMER +class wxMacToolTipTimer : public wxTimer +{ +public: + wxMacToolTipTimer(wxMacToolTip* tip, int iMilliseconds) ; + wxMacToolTipTimer() {} ; + virtual ~wxMacToolTipTimer() {} ; + + void Notify() + { + if ( m_mark == m_tip->GetMark() ) + m_tip->Draw() ; + } + +protected: + wxMacToolTip* m_tip; + long m_mark ; +}; +#endif // wxUSE_TIMER + +//----------------------------------------------------------------------------- +// wxToolTip +//----------------------------------------------------------------------------- +static long s_ToolTipDelay = 500 ; +static bool s_ShowToolTips = true ; +static wxMacToolTip s_ToolTip ; +static wxWindow* s_LastWindowEntered = NULL ; +static wxRect2DInt s_ToolTipArea ; +static WindowRef s_ToolTipWindowRef = NULL ; + +IMPLEMENT_ABSTRACT_CLASS(wxToolTip, wxObject) + + +wxToolTip::wxToolTip( const wxString &tip ) +{ + m_text = tip; + m_window = (wxWindow*) NULL; +} + +wxToolTip::~wxToolTip() +{ +} + +void wxToolTip::SetTip( const wxString &tip ) +{ + m_text = tip; + + if ( m_window ) + { +#if 0 + // update it immediately + wxToolInfo ti(GetHwndOf(m_window)); + ti.lpszText = (wxChar *)m_text.c_str(); + + (void)SendTooltipMessage(GetToolTipCtrl(), TTM_UPDATETIPTEXT, 0, &ti); +#endif + } +} + +void wxToolTip::SetWindow( wxWindow *win ) +{ + m_window = win ; +} + +void wxToolTip::Enable( bool flag ) +{ + if ( s_ShowToolTips != flag ) + { + s_ShowToolTips = flag ; + + if ( s_ShowToolTips ) + { + } + else + { + s_ToolTip.Clear() ; + } + } +} + +void wxToolTip::SetDelay( long msecs ) +{ + s_ToolTipDelay = msecs ; +} + +void wxToolTip::SetAutoPop( long WXUNUSED(msecs) ) +{ +} + +void wxToolTip::SetReshow( long WXUNUSED(msecs) ) +{ +} + +void wxToolTip::RelayEvent( wxWindow *win , wxMouseEvent &event ) +{ + if ( s_ShowToolTips ) + { + if ( event.GetEventType() == wxEVT_LEAVE_WINDOW ) + { + s_ToolTip.Clear() ; + } + else if (event.GetEventType() == wxEVT_ENTER_WINDOW || event.GetEventType() == wxEVT_MOTION ) + { + wxPoint2DInt where( event.m_x , event.m_y ) ; + if ( s_LastWindowEntered == win && s_ToolTipArea.Contains( where ) ) + { + } + else + { + s_ToolTip.Clear() ; + s_ToolTipArea = wxRect2DInt( event.m_x - 2 , event.m_y - 2 , 4 , 4 ) ; + s_LastWindowEntered = win ; + + WindowRef window = MAC_WXHWND( win->MacGetTopLevelWindowRef() ) ; + int x = event.m_x ; + int y = event.m_y ; + wxPoint local( x , y ) ; + win->MacClientToRootWindow( &x, &y ) ; + wxPoint windowlocal( x , y ) ; + s_ToolTip.Setup( window , win->MacGetToolTipString( local ) , windowlocal ) ; + } + } + } +} + +void wxToolTip::RemoveToolTips() +{ + s_ToolTip.Clear() ; +} + +// --- mac specific +#if wxUSE_TIMER +wxMacToolTipTimer::wxMacToolTipTimer( wxMacToolTip *tip , int msec ) +{ + m_tip = tip; + m_mark = tip->GetMark() ; + Start(msec, true); +} +#endif // wxUSE_TIMER + +wxMacToolTip::wxMacToolTip() +{ + m_window = NULL ; + m_backpict = NULL ; +#if wxUSE_TIMER + m_timer = NULL ; +#endif + m_mark = 0 ; + m_shown = false ; +} + +void wxMacToolTip::Setup( WindowRef win , const wxString& text , const wxPoint& localPosition ) +{ + m_mark++ ; + + Clear() ; + m_position = localPosition ; + m_label = text ; + m_window =win; + s_ToolTipWindowRef = m_window ; + m_backpict = NULL ; +#if wxUSE_TIMER + if ( m_timer ) + delete m_timer ; + + m_timer = new wxMacToolTipTimer( this , s_ToolTipDelay ) ; +#endif // wxUSE_TIMER +} + +wxMacToolTip::~wxMacToolTip() +{ +#if wxUSE_TIMER + if ( m_timer ) + { + delete m_timer ; + m_timer = NULL; + } +#endif // wxUSE_TIMER + if ( m_backpict ) + Clear() ; +} + +const short kTipBorder = 2 ; +const short kTipOffset = 5 ; + +void wxMacToolTip::Draw() +{ + if ( m_label.empty() ) + return ; + + if ( m_window == s_ToolTipWindowRef ) + { + m_shown = true ; + + HMHelpContentRec tag ; + tag.version = kMacHelpVersion; + + Point p = { m_position.y , m_position.x }; + wxMacLocalToGlobal( m_window , &p ) ; + SetRect( &tag.absHotRect , p.h - 2 , p.v - 2 , p.h + 2 , p.v + 2 ); + + m_helpTextRef = wxCFStringRef( m_label , wxFONTENCODING_DEFAULT ) ; + tag.content[kHMMinimumContentIndex].contentType = kHMCFStringContent ; + tag.content[kHMMinimumContentIndex].u.tagCFString = m_helpTextRef ; + tag.content[kHMMaximumContentIndex].contentType = kHMCFStringContent ; + tag.content[kHMMaximumContentIndex].u.tagCFString = m_helpTextRef ; + tag.tagSide = kHMDefaultSide; + HMDisplayTag( &tag ); + } +} + +void wxToolTip::NotifyWindowDelete( WXHWND win ) +{ + if ( win == s_ToolTipWindowRef ) + s_ToolTipWindowRef = NULL ; +} + +void wxMacToolTip::Clear() +{ + m_mark++ ; +#if wxUSE_TIMER + if ( m_timer ) + { + delete m_timer ; + m_timer = NULL ; + } +#endif // wxUSE_TIMER + if ( !m_shown ) + return ; + + HMHideTag() ; +} + +#endif // wxUSE_TOOLTIPS diff --git a/src/osx/carbon/toplevel.cpp b/src/osx/carbon/toplevel.cpp new file mode 100644 index 0000000000..92243b278d --- /dev/null +++ b/src/osx/carbon/toplevel.cpp @@ -0,0 +1,274 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/toplevel.cpp +// Purpose: implements wxTopLevelWindow for Mac +// Author: Stefan Csomor +// Modified by: +// Created: 24.09.01 +// RCS-ID: $Id$ +// Copyright: (c) 2001-2004 Stefan Csomor +// License: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#include "wx/toplevel.h" + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/frame.h" + #include "wx/string.h" + #include "wx/log.h" + #include "wx/intl.h" + #include "wx/settings.h" + #include "wx/strconv.h" + #include "wx/control.h" +#endif //WX_PRECOMP + +#include "wx/mac/uma.h" +#include "wx/tooltip.h" +#include "wx/dnd.h" + +#if wxUSE_SYSTEM_OPTIONS + #include "wx/sysopt.h" +#endif + +#ifndef __DARWIN__ +#include +#endif + +// for targeting OSX +#include "wx/mac/private.h" + +// ============================================================================ +// wxTopLevelWindowMac implementation +// ============================================================================ + +BEGIN_EVENT_TABLE(wxTopLevelWindowMac, wxTopLevelWindowBase) +END_EVENT_TABLE() + +// ---------------------------------------------------------------------------- +// wxTopLevelWindowMac creation +// ---------------------------------------------------------------------------- + +typedef struct +{ + wxPoint m_position ; + wxSize m_size ; + bool m_wasResizable ; +} FullScreenData ; + +void wxTopLevelWindowMac::Init() +{ + m_iconized = + m_maximizeOnShow = false; + m_macFullScreenData = NULL ; +} + +bool wxTopLevelWindowMac::Create(wxWindow *parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) +{ + if ( !wxNonOwnedWindow::Create(parent, id, pos, size, style, name) ) + return false; + + wxWindow::SetLabel( title ) ; + SetWindowTitleWithCFString( (WindowRef) m_macWindow , wxCFStringRef( title , GetFont().GetEncoding() ) ); + wxTopLevelWindows.Append(this); + + return true; +} + +wxTopLevelWindowMac::~wxTopLevelWindowMac() +{ + FullScreenData *data = (FullScreenData *) m_macFullScreenData ; + delete data ; + m_macFullScreenData = NULL ; +} + + +// ---------------------------------------------------------------------------- +// wxTopLevelWindowMac maximize/minimize +// ---------------------------------------------------------------------------- + +void wxTopLevelWindowMac::Maximize(bool maximize) +{ + Point idealSize = { 0 , 0 } ; + if ( maximize ) + { +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + HIRect bounds ; + HIWindowGetAvailablePositioningBounds(kCGNullDirectDisplay,kHICoordSpace72DPIGlobal, + &bounds); + idealSize.h = bounds.size.width; + idealSize.v = bounds.size.height; +#else + Rect rect ; + GetAvailableWindowPositioningBounds(GetMainDevice(),&rect) ; + idealSize.h = rect.right - rect.left ; + idealSize.v = rect.bottom - rect.top ; +#endif + } + ZoomWindowIdeal( (WindowRef)m_macWindow , maximize ? inZoomOut : inZoomIn , &idealSize ) ; +} + +bool wxTopLevelWindowMac::IsMaximized() const +{ + return IsWindowInStandardState( (WindowRef)m_macWindow , NULL , NULL ) ; +} + +void wxTopLevelWindowMac::Iconize(bool iconize) +{ + if ( IsWindowCollapsable( (WindowRef)m_macWindow) ) + CollapseWindow( (WindowRef)m_macWindow , iconize ) ; +} + +bool wxTopLevelWindowMac::IsIconized() const +{ + return IsWindowCollapsed((WindowRef)m_macWindow ) ; +} + +void wxTopLevelWindowMac::Restore() +{ + if ( IsMaximized() ) + Maximize(false); + else if ( IsIconized() ) + Iconize(false); +} + +// ---------------------------------------------------------------------------- +// wxTopLevelWindowMac misc +// ---------------------------------------------------------------------------- + +wxPoint wxTopLevelWindowMac::GetClientAreaOrigin() const +{ + return wxPoint(0, 0) ; +} + +void wxTopLevelWindowMac::SetTitle(const wxString& title) +{ + wxWindow::SetLabel( title ) ; + SetWindowTitleWithCFString( (WindowRef) m_macWindow , wxCFStringRef( title , GetFont().GetEncoding() ) ) ; +} + +wxString wxTopLevelWindowMac::GetTitle() const +{ + return wxWindow::GetLabel(); +} + +bool wxTopLevelWindowMac::ShowFullScreen(bool show, long style) +{ + if ( show ) + { + FullScreenData *data = (FullScreenData *)m_macFullScreenData ; + delete data ; + data = new FullScreenData() ; + + m_macFullScreenData = data ; + data->m_position = GetPosition() ; + data->m_size = GetSize() ; + data->m_wasResizable = MacGetWindowAttributes() & kWindowResizableAttribute ; + + if ( style & wxFULLSCREEN_NOMENUBAR ) + HideMenuBar() ; + + wxRect client = wxGetClientDisplayRect() ; + + int left , top , right , bottom ; + int x, y, w, h ; + + x = client.x ; + y = client.y ; + w = client.width ; + h = client.height ; + + MacGetContentAreaInset( left , top , right , bottom ) ; + + if ( style & wxFULLSCREEN_NOCAPTION ) + { + y -= top ; + h += top ; + } + + if ( style & wxFULLSCREEN_NOBORDER ) + { + x -= left ; + w += left + right ; + h += bottom ; + } + + if ( style & wxFULLSCREEN_NOTOOLBAR ) + { + // TODO + } + + if ( style & wxFULLSCREEN_NOSTATUSBAR ) + { + // TODO + } + + SetSize( x , y , w, h ) ; + if ( data->m_wasResizable ) + MacChangeWindowAttributes( kWindowNoAttributes , kWindowResizableAttribute ) ; + } + else if ( m_macFullScreenData != NULL ) + { + ShowMenuBar() ; + FullScreenData *data = (FullScreenData *) m_macFullScreenData ; + if ( data->m_wasResizable ) + MacChangeWindowAttributes( kWindowResizableAttribute , kWindowNoAttributes ) ; + SetPosition( data->m_position ) ; + SetSize( data->m_size ) ; + + delete data ; + m_macFullScreenData = NULL ; + } + + return false; +} + +bool wxTopLevelWindowMac::IsFullScreen() const +{ + return m_macFullScreenData != NULL ; +} + +// Attracts the users attention to this window if the application is +// inactive (should be called when a background event occurs) + +static pascal void wxMacNMResponse( NMRecPtr ptr ) +{ + NMRemove( ptr ) ; + DisposePtr( (Ptr)ptr ) ; +} + +void wxTopLevelWindowMac::RequestUserAttention(int WXUNUSED(flags)) +{ + NMRecPtr notificationRequest = (NMRecPtr) NewPtr( sizeof( NMRec) ) ; + static wxMacNMUPP nmupp( wxMacNMResponse ); + + memset( notificationRequest , 0 , sizeof(*notificationRequest) ) ; + notificationRequest->qType = nmType ; + notificationRequest->nmMark = 1 ; + notificationRequest->nmIcon = 0 ; + notificationRequest->nmSound = 0 ; + notificationRequest->nmStr = NULL ; + notificationRequest->nmResp = nmupp ; + + verify_noerr( NMInstall( notificationRequest ) ) ; +} diff --git a/src/osx/carbon/treectrl.cpp b/src/osx/carbon/treectrl.cpp new file mode 100644 index 0000000000..82a90b0854 --- /dev/null +++ b/src/osx/carbon/treectrl.cpp @@ -0,0 +1,418 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/treectrl.cpp +// Purpose: wxTreeCtrl. See also Robert's generic wxTreeCtrl. +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/stubs/textctrl.h" +#include "wx/treebase.h" +#include "wx/stubs/treectrl.h" + + +IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxTreeItem, wxObject) + + +wxTreeCtrl::wxTreeCtrl() +{ + m_imageListNormal = NULL; + m_imageListState = NULL; + m_textCtrl = NULL; +} + +bool wxTreeCtrl::Create(wxWindow *parent, + wxWindowID id, const wxPoint& pos, const wxSize& size, + long style, const wxValidator& wxVALIDATOR_PARAM(validator), + const wxString& name) +{ + m_imageListNormal = NULL; + m_imageListState = NULL; + m_textCtrl = NULL; + + SetName(name); +#if wxUSE_VALIDATORS + SetValidator(validator); +#endif + SetParent(parent); + + m_windowStyle = style; + + m_windowId = (id == wxID_ANY) ? NewControlId() : id; + + if (parent) + parent->AddChild(this); + + // TODO: create tree control + + return false; +} + +wxTreeCtrl::~wxTreeCtrl() +{ + delete m_textCtrl; +} + +// Attributes +unsigned int wxTreeCtrl::GetCount() const +{ + // TODO + return 0; +} + +int wxTreeCtrl::GetIndent() const +{ + // TODO + return 0; +} + +void wxTreeCtrl::SetIndent(int indent) +{ + // TODO +} + +wxImageList *wxTreeCtrl::GetImageList(int which) const +{ + if ( which == wxIMAGE_LIST_NORMAL ) + return m_imageListNormal; + else if ( which == wxIMAGE_LIST_STATE ) + return m_imageListState; + + return NULL; +} + +void wxTreeCtrl::SetImageList(wxImageList *imageList, int which) +{ + if ( which == wxIMAGE_LIST_NORMAL ) + m_imageListNormal = imageList; + else if ( which == wxIMAGE_LIST_STATE ) + m_imageListState = imageList; + + // TODO +} + +long wxTreeCtrl::GetNextItem(long item, int code) const +{ + // TODO + return 0; +} + +bool wxTreeCtrl::ItemHasChildren(long item) const +{ + // TODO + return false; +} + +long wxTreeCtrl::GetChild(long item) const +{ + // TODO + return 0; +} + +long wxTreeCtrl::GetItemParent(long item) const +{ + // TODO + return 0; +} + +long wxTreeCtrl::GetFirstVisibleItem() const +{ + // TODO + return 0; +} + +long wxTreeCtrl::GetNextVisibleItem(long item) const +{ + // TODO + return 0; +} + +long wxTreeCtrl::GetSelection() const +{ + // TODO + return 0; +} + +long wxTreeCtrl::GetRootItem() const +{ + // TODO + return 0; +} + +bool wxTreeCtrl::GetItem(wxTreeItem& info) const +{ + // TODO + return false; +} + +bool wxTreeCtrl::SetItem(wxTreeItem& info) +{ + // TODO + return false; +} + +int wxTreeCtrl::GetItemState(long item, long stateMask) const +{ + wxTreeItem info; + + info.m_mask = wxTREE_MASK_STATE; + info.m_stateMask = stateMask; + info.m_itemId = item; + + if (!GetItem(info)) + return 0; + + return info.m_state; +} + +bool wxTreeCtrl::SetItemState(long item, long state, long stateMask) +{ + wxTreeItem info; + + info.m_mask = wxTREE_MASK_STATE; + info.m_state = state; + info.m_stateMask = stateMask; + info.m_itemId = item; + + return SetItem(info); +} + +bool wxTreeCtrl::SetItemImage(long item, int image, int selImage) +{ + wxTreeItem info; + + info.m_mask = wxTREE_MASK_IMAGE; + info.m_image = image; + if ( selImage > -1) + { + info.m_selectedImage = selImage; + info.m_mask |= wxTREE_MASK_SELECTED_IMAGE; + } + + info.m_itemId = item; + + return SetItem(info); +} + +wxString wxTreeCtrl::GetItemText(long item) const +{ + wxTreeItem info; + + info.m_mask = wxTREE_MASK_TEXT; + info.m_itemId = item; + + if (!GetItem(info)) + return wxEmptyString; + + return info.m_text; +} + +void wxTreeCtrl::SetItemText(long item, const wxString& str) +{ + wxTreeItem info; + + info.m_mask = wxTREE_MASK_TEXT; + info.m_itemId = item; + info.m_text = str; + + SetItem(info); +} + +long wxTreeCtrl::GetItemData(long item) const +{ + wxTreeItem info; + + info.m_mask = wxTREE_MASK_DATA; + info.m_itemId = item; + + if (!GetItem(info)) + return 0; + + return info.m_data; +} + +bool wxTreeCtrl::SetItemData(long item, long data) +{ + wxTreeItem info; + + info.m_mask = wxTREE_MASK_DATA; + info.m_itemId = item; + info.m_data = data; + + return SetItem(info); +} + +bool wxTreeCtrl::GetItemRect(long item, wxRect& rect, bool textOnly) const +{ + // TODO + return false; +} + +wxTextCtrl * wxTreeCtrl::GetEditControl() const +{ + return m_textCtrl; +} + +// Operations +bool wxTreeCtrl::DeleteItem(long item) +{ + // TODO + return false; +} + +bool wxTreeCtrl::ExpandItem(long item, int action) +{ + // TODO + switch ( action ) + { + case wxTREE_EXPAND_EXPAND: + break; + + case wxTREE_EXPAND_COLLAPSE: + break; + + case wxTREE_EXPAND_COLLAPSE_RESET: + break; + + case wxTREE_EXPAND_TOGGLE: + break; + + default: + wxFAIL_MSG(wxT("unknown action in wxTreeCtrl::ExpandItem"); + } + + // TODO: expand item + bool bOk = false; + + // May not send messages, so emulate them + if ( bOk ) + { + wxTreeEvent event(wxEVT_NULL, m_windowId); + event.m_item.m_itemId = item; + event.m_item.m_mask = event.m_item.m_stateMask = 0xFFFF; // get all + GetItem(event.m_item); + + bool bIsExpanded = (event.m_item.m_state & wxTREE_STATE_EXPANDED) != 0; + + event.m_code = action; + event.SetEventObject(this); + + // @@@ return values of {EXPAND|COLLAPS}ING event handler is discarded + event.SetEventType( + bIsExpanded + ? wxEVT_COMMAND_TREE_ITEM_EXPANDING + : wxEVT_COMMAND_TREE_ITEM_COLLAPSING); + HandleWindowEvent(event); + + event.SetEventType( + bIsExpanded + ? wxEVT_COMMAND_TREE_ITEM_EXPANDED + : wxEVT_COMMAND_TREE_ITEM_COLLAPSED); + HandleWindowEvent(event); + } + + return bOk; +} + +long wxTreeCtrl::InsertItem(long parent, wxTreeItem& info, long insertAfter) +{ + // TODO + return 0; +} + +long wxTreeCtrl::InsertItem(long parent, const wxString& label, + int image, int selImage, long insertAfter) +{ + wxTreeItem info; + info.m_text = label; + info.m_mask = wxTREE_MASK_TEXT; + if ( image > -1 ) + { + info.m_mask |= wxTREE_MASK_IMAGE | wxTREE_MASK_SELECTED_IMAGE; + info.m_image = image; + if ( selImage == -1 ) + info.m_selectedImage = image; + else + info.m_selectedImage = selImage; + } + + return InsertItem(parent, info, insertAfter); +} + +bool wxTreeCtrl::SelectItem(long item) +{ + // TODO + return false; +} + +bool wxTreeCtrl::ScrollTo(long item) +{ + // TODO + return false; +} + +bool wxTreeCtrl::DeleteAllItems() +{ + // TODO + return false; +} + +wxTextCtrl * wxTreeCtrl::EditLabel(long item, wxClassInfo* textControlClass) +{ + // TODO + return NULL; +} + +// End label editing, optionally cancelling the edit +bool wxTreeCtrl::EndEditLabel(bool cancel) +{ + // TODO + return false; +} + +long wxTreeCtrl::HitTest(const wxPoint& point, int& flags) +{ + // TODO + return 0; +} + +bool wxTreeCtrl::SortChildren(long item) +{ + // TODO + return false; +} + +bool wxTreeCtrl::EnsureVisible(long item) +{ + // TODO + return false; +} + +// Tree item structure +wxTreeItem::wxTreeItem() +{ + m_mask = 0; + m_itemId = 0; + m_state = 0; + m_stateMask = 0; + m_image = -1; + m_selectedImage = -1; + m_children = 0; + m_data = 0; +} + +// Tree event +IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxCommandEvent) + +wxTreeEvent::wxTreeEvent(wxEventType commandType, int id) + : wxCommandEvent(commandType, id) +{ + m_code = 0; + m_oldItem = 0; +} diff --git a/src/osx/carbon/uma.cpp b/src/osx/carbon/uma.cpp new file mode 100644 index 0000000000..634ea0e03f --- /dev/null +++ b/src/osx/carbon/uma.cpp @@ -0,0 +1,316 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/uma.cpp +// Purpose: UMA support +// Author: Stefan Csomor +// Modified by: +// Created: 04/01/98 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: The wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/mac/uma.h" + +#if wxUSE_GUI + +#include "wx/toplevel.h" +#include "wx/dc.h" + +#include "wx/mac/uma.h" + +static SInt32 sUMASystemVersion = 0 ; + +long UMAGetSystemVersion() +{ + if ( sUMASystemVersion == 0 ) + { + verify_noerr(Gestalt(gestaltSystemVersion, &sUMASystemVersion)); + } + return sUMASystemVersion ; +} + +void UMAInitToolbox( UInt16 WXUNUSED(inMoreMastersCalls), + bool WXUNUSED(isEmbedded) ) +{ +#if 0 // ndef __LP64__ + { + FontFamilyID fontId ; + Str255 fontName ; + SInt16 fontSize ; + Style fontStyle ; + + GetThemeFont(kThemeSmallSystemFont , GetApplicationScript() , fontName , &fontSize , &fontStyle ) ; + GetFNum( fontName, &fontId ); + + TXNMacOSPreferredFontDescription fontDescriptions[] = + { + { fontId , (fontSize << 16) , kTXNDefaultFontStyle, kTXNSystemDefaultEncoding } + } ; + int noOfFontDescriptions = sizeof( fontDescriptions ) / sizeof(TXNMacOSPreferredFontDescription) ; + + OptionBits options = 0 ; + + TXNInitTextension( fontDescriptions, noOfFontDescriptions, options ); + } +#endif +} + +// menu manager + +#if 1 // not yet wxMAC_USE_COCOA == 0 + +MenuRef UMANewMenu( SInt16 id , const wxString& title , wxFontEncoding encoding ) +{ + wxString str = wxStripMenuCodes( title ) ; + MenuRef menu ; + + CreateNewMenu( id , 0 , &menu ) ; + SetMenuTitleWithCFString( menu , wxCFStringRef(str , encoding ) ) ; + + return menu ; +} + +void UMASetMenuTitle( MenuRef menu , const wxString& title , wxFontEncoding encoding ) +{ + wxString str = wxStripMenuCodes( title ) ; + + SetMenuTitleWithCFString( menu , wxCFStringRef(str , encoding) ) ; +} + +void UMASetMenuItemText( MenuRef menu, MenuItemIndex item, const wxString& title, wxFontEncoding encoding ) +{ + // we don't strip the accels here anymore, must be done before + wxString str = title ; + + SetMenuItemTextWithCFString( menu , item , wxCFStringRef(str , encoding) ) ; +} + +void UMAEnableMenuItem( MenuRef inMenu , MenuItemIndex inItem , bool enable) +{ + if ( enable ) + EnableMenuItem( inMenu , inItem ) ; + else + DisableMenuItem( inMenu , inItem ) ; +} + +void UMAAppendSubMenuItem( MenuRef menu , const wxString& title, wxFontEncoding encoding , SInt16 id ) +{ + AppendMenuItemTextWithCFString( menu, + CFSTR("A"), 0, 0,NULL); + UMASetMenuItemText( menu, (SInt16) ::CountMenuItems(menu), title , encoding ); + SetMenuItemHierarchicalID( menu , CountMenuItems( menu ) , id ) ; +} + +void UMAInsertSubMenuItem( MenuRef menu , const wxString& title, wxFontEncoding encoding , MenuItemIndex item , SInt16 id ) +{ + InsertMenuItemTextWithCFString( menu, + CFSTR("A"), item, 0, 0); + + UMASetMenuItemText( menu, item+1, title , encoding ); + SetMenuItemHierarchicalID( menu , item+1 , id ) ; +} + +void UMASetMenuItemShortcut( MenuRef menu , MenuItemIndex item , wxAcceleratorEntry *entry ) +{ + if ( !entry ) + return ; + + UInt8 modifiers = 0 ; + SInt16 key = entry->GetKeyCode() ; + if ( key ) + { + bool explicitCommandKey = (entry->GetFlags() & wxACCEL_CTRL); + + if (entry->GetFlags() & wxACCEL_ALT) + modifiers |= kMenuOptionModifier ; + + if (entry->GetFlags() & wxACCEL_SHIFT) + modifiers |= kMenuShiftModifier ; + + SInt16 glyph = 0 ; + SInt16 macKey = key ; + if ( key >= WXK_F1 && key <= WXK_F15 ) + { + if ( !explicitCommandKey ) + modifiers |= kMenuNoCommandModifier ; + + // for some reasons this must be 0 right now + // everything else leads to just the first function key item + // to be selected. Thanks to Ryan Wilcox for finding out. + macKey = 0 ; + glyph = kMenuF1Glyph + ( key - WXK_F1 ) ; + if ( key >= WXK_F13 ) + glyph += 12 ; + } + else + { + switch ( key ) + { + case WXK_BACK : + macKey = kBackspaceCharCode ; + glyph = kMenuDeleteLeftGlyph ; + break ; + + case WXK_TAB : + macKey = kTabCharCode ; + glyph = kMenuTabRightGlyph ; + break ; + + case kEnterCharCode : + macKey = kEnterCharCode ; + glyph = kMenuEnterGlyph ; + break ; + + case WXK_RETURN : + macKey = kReturnCharCode ; + glyph = kMenuReturnGlyph ; + break ; + + case WXK_ESCAPE : + macKey = kEscapeCharCode ; + glyph = kMenuEscapeGlyph ; + break ; + + case WXK_SPACE : + macKey = ' ' ; + glyph = kMenuSpaceGlyph ; + break ; + + case WXK_DELETE : + macKey = kDeleteCharCode ; + glyph = kMenuDeleteRightGlyph ; + break ; + + case WXK_CLEAR : + macKey = kClearCharCode ; + glyph = kMenuClearGlyph ; + break ; + + case WXK_PAGEUP : + macKey = kPageUpCharCode ; + glyph = kMenuPageUpGlyph ; + break ; + + case WXK_PAGEDOWN : + macKey = kPageDownCharCode ; + glyph = kMenuPageDownGlyph ; + break ; + + case WXK_LEFT : + macKey = kLeftArrowCharCode ; + glyph = kMenuLeftArrowGlyph ; + break ; + + case WXK_UP : + macKey = kUpArrowCharCode ; + glyph = kMenuUpArrowGlyph ; + break ; + + case WXK_RIGHT : + macKey = kRightArrowCharCode ; + glyph = kMenuRightArrowGlyph ; + break ; + + case WXK_DOWN : + macKey = kDownArrowCharCode ; + glyph = kMenuDownArrowGlyph ; + break ; + + case WXK_HOME : + macKey = kHomeCharCode ; + glyph = kMenuNorthwestArrowGlyph ; + break ; + + case WXK_END : + macKey = kEndCharCode ; + glyph = kMenuSoutheastArrowGlyph ; + break ; + default : + macKey = toupper( key ) ; + break ; + } + + // we now allow non command key shortcuts + // remove in case this gives problems + if ( !explicitCommandKey ) + modifiers |= kMenuNoCommandModifier ; + } + + // 1d and 1e have special meaning to SetItemCmd, so + // do not use for these character codes. + if (key != WXK_UP && key != WXK_RIGHT && key != WXK_DOWN && key != WXK_LEFT) + SetItemCmd( menu, item , macKey ); + + SetMenuItemModifiers( menu, item , modifiers ) ; + + if ( glyph ) + SetMenuItemKeyGlyph( menu, item , glyph ) ; + } +} + +void UMAAppendMenuItem( MenuRef menu , const wxString& title, wxFontEncoding encoding , wxAcceleratorEntry *entry ) +{ + AppendMenuItemTextWithCFString( menu, + CFSTR("A"), 0, 0,NULL); + // don't attempt to interpret metacharacters like a '-' at the beginning (would become a separator otherwise) + ChangeMenuItemAttributes( menu , ::CountMenuItems(menu), kMenuItemAttrIgnoreMeta , 0 ) ; + UMASetMenuItemText(menu, (SInt16) ::CountMenuItems(menu), title , encoding ); + UMASetMenuItemShortcut( menu , (SInt16) ::CountMenuItems(menu), entry ) ; +} + +void UMAInsertMenuItem( MenuRef menu , const wxString& title, wxFontEncoding encoding , MenuItemIndex item , wxAcceleratorEntry *entry ) +{ + InsertMenuItemTextWithCFString( menu, + CFSTR("A"), item, 0, 0); + + // don't attempt to interpret metacharacters like a '-' at the beginning (would become a separator otherwise) + ChangeMenuItemAttributes( menu , item+1, kMenuItemAttrIgnoreMeta , 0 ) ; + UMASetMenuItemText(menu, item+1 , title , encoding ); + UMASetMenuItemShortcut( menu , item+1 , entry ) ; +} + +#endif + +#if 1 // not yet wxMAC_USE_COCOA == 0 + +static OSStatus UMAGetHelpMenu( + MenuRef * outHelpMenu, + MenuItemIndex * outFirstCustomItemIndex, + bool allowHelpMenuCreation); + +static OSStatus UMAGetHelpMenu( + MenuRef * outHelpMenu, + MenuItemIndex * outFirstCustomItemIndex, + bool allowHelpMenuCreation) +{ + static bool s_createdHelpMenu = false ; + + if ( !s_createdHelpMenu && !allowHelpMenuCreation ) + { + return paramErr ; + } + + OSStatus status = HMGetHelpMenu( outHelpMenu , outFirstCustomItemIndex ) ; + s_createdHelpMenu = ( status == noErr ) ; + return status ; +} + +OSStatus UMAGetHelpMenu( + MenuRef * outHelpMenu, + MenuItemIndex * outFirstCustomItemIndex) +{ + return UMAGetHelpMenu( outHelpMenu , outFirstCustomItemIndex , true ); +} + +OSStatus UMAGetHelpMenuDontCreate( + MenuRef * outHelpMenu, + MenuItemIndex * outFirstCustomItemIndex) +{ + return UMAGetHelpMenu( outHelpMenu , outFirstCustomItemIndex , false ); +} + +#endif + +#endif // wxUSE_GUI diff --git a/src/osx/carbon/utils.cpp b/src/osx/carbon/utils.cpp new file mode 100644 index 0000000000..b690bac889 --- /dev/null +++ b/src/osx/carbon/utils.cpp @@ -0,0 +1,2010 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/utils.cpp +// Purpose: Various utilities +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/utils.h" + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/app.h" + #if wxUSE_GUI + #include "wx/toplevel.h" + #include "wx/font.h" + #endif +#endif + +#include "wx/apptrait.h" + +#if wxUSE_GUI + #include "wx/mac/uma.h" +#endif + +#include + +#include +#include +#include +#include + +// #include "MoreFilesX.h" + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + #include +#endif + +#if wxUSE_GUI + #include + #include + #include "wx/mac/private/timer.h" +#endif // wxUSE_GUI + +#include "wx/evtloop.h" +#include "wx/mac/private.h" + +#if defined(__MWERKS__) && wxUSE_UNICODE +#if __MWERKS__ < 0x4100 + #include +#endif +#endif + +#if wxUSE_BASE + +// our OS version is the same in non GUI and GUI cases +wxOperatingSystemId wxGetOsVersion(int *majorVsn, int *minorVsn) +{ + SInt32 theSystem; + Gestalt(gestaltSystemVersion, &theSystem); + + if ( majorVsn != NULL ) + *majorVsn = (theSystem >> 8); + + if ( minorVsn != NULL ) + *minorVsn = (theSystem & 0xFF); + +#if defined( __DARWIN__ ) + return wxOS_MAC_OSX_DARWIN; +#else + return wxOS_MAC_OS; +#endif +} + +extern bool WXDLLEXPORT wxIsDebuggerRunning() +{ + // TODO : try to find out ... + return false; +} + +// Emit a beeeeeep +void wxBell() +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if ( AudioServicesPlayAlertSound ) + AudioServicesPlayAlertSound(kUserPreferredAlert); + else +#endif +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + SysBeep(30); +#else + { + } +#endif +} + + +#endif // wxUSE_BASE + +#if wxUSE_GUI + +wxPortId wxGUIAppTraits::GetToolkitVersion(int *verMaj, int *verMin) const +{ + // We suppose that toolkit version is the same as OS version under Mac + wxGetOsVersion(verMaj, verMin); + + return wxPORT_MAC; +} + +wxEventLoopBase* wxGUIAppTraits::CreateEventLoop() +{ + return new wxEventLoop; +} + +wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer) +{ + return new wxCarbonTimerImpl(timer); +} + +int gs_wxBusyCursorCount = 0; +extern wxCursor gMacCurrentCursor; +wxCursor gMacStoredActiveCursor; + +// Set the cursor to the busy cursor for all windows +void wxBeginBusyCursor(const wxCursor *cursor) +{ + if (gs_wxBusyCursorCount++ == 0) + { + gMacStoredActiveCursor = gMacCurrentCursor; + cursor->MacInstall(); + + wxSetCursor(*cursor); + } + //else: nothing to do, already set +} + +// Restore cursor to normal +void wxEndBusyCursor() +{ + wxCHECK_RET( gs_wxBusyCursorCount > 0, + wxT("no matching wxBeginBusyCursor() for wxEndBusyCursor()") ); + + if (--gs_wxBusyCursorCount == 0) + { + gMacStoredActiveCursor.MacInstall(); + gMacStoredActiveCursor = wxNullCursor; + + wxSetCursor(wxNullCursor); + } +} + +// true if we're between the above two calls +bool wxIsBusy() +{ + return (gs_wxBusyCursorCount > 0); +} + +#endif // wxUSE_GUI + +#if wxUSE_BASE + +wxString wxMacFindFolderNoSeparator( short vol, + OSType folderType, + Boolean createFolder) +{ + FSRef fsRef; + wxString strDir; + + if ( FSFindFolder( vol, folderType, createFolder, &fsRef) == noErr) + { + strDir = wxMacFSRefToPath( &fsRef ); + } + + return strDir; +} + +wxString wxMacFindFolder( short vol, + OSType folderType, + Boolean createFolder) +{ + return wxMacFindFolderNoSeparator(vol, folderType, createFolder) + wxFILE_SEP_PATH; +} + +#endif // wxUSE_BASE + +#if wxUSE_GUI + +// Check whether this window wants to process messages, e.g. Stop button +// in long calculations. +bool wxCheckForInterrupt(wxWindow *WXUNUSED(wnd)) +{ + // TODO + return false; +} + +void wxGetMousePosition( int* x, int* y ) +{ +#if wxMAC_USE_QUICKDRAW + Point pt; + GetGlobalMouse(&pt); + *x = pt.h; + *y = pt.v; +#else + // TODO +#endif +}; + +// Return true if we have a colour display +bool wxColourDisplay() +{ + return true; +} + +// Returns depth of screen +int wxDisplayDepth() +{ +#if wxMAC_USE_QUICKDRAW + int theDepth = (int) CGDisplayBitsPerPixel(CGMainDisplayID()); + Rect globRect; + SetRect(&globRect, -32760, -32760, 32760, 32760); + GDHandle theMaxDevice; + + theMaxDevice = GetMaxDevice(&globRect); + if (theMaxDevice != NULL) + theDepth = (**(**theMaxDevice).gdPMap).pixelSize; + + return theDepth; +#else + return 32; // TODO +#endif +} + +// Get size of display +void wxDisplaySize(int *width, int *height) +{ + // TODO adapt for multi-displays + CGRect bounds = CGDisplayBounds(CGMainDisplayID()); + if ( width ) + *width = (int)bounds.size.width ; + if ( height ) + *height = (int)bounds.size.height; +} + +void wxDisplaySizeMM(int *width, int *height) +{ + wxDisplaySize(width, height); + // on mac 72 is fixed (at least now;-) + float cvPt2Mm = 25.4 / 72; + + if (width != NULL) + *width = int( *width * cvPt2Mm ); + + if (height != NULL) + *height = int( *height * cvPt2Mm ); +} + +void wxClientDisplayRect(int *x, int *y, int *width, int *height) +{ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +#if wxMAC_USE_QUICKDRAW + + HIRect bounds ; + HIWindowGetAvailablePositioningBounds(kCGNullDirectDisplay,kHICoordSpace72DPIGlobal, + &bounds); + if ( x ) + *x = bounds.origin.x; + if ( y ) + *y = bounds.origin.y; + if ( width ) + *width = bounds.size.width; + if ( height ) + *height = bounds.size.height; +#else + int w, h; + wxDisplaySize(&w,&h); + if ( x ) + *x = 0; + if ( y ) + *y = 24; + if ( width ) + *width = w; + if ( height ) + *height = h-24; +#endif +#else + Rect r; + GetAvailableWindowPositioningBounds( GetMainDevice() , &r ); + if ( x ) + *x = r.left; + if ( y ) + *y = r.top; + if ( width ) + *width = r.right - r.left; + if ( height ) + *height = r.bottom - r.top; +#endif +} + +wxWindow* wxFindWindowAtPoint(const wxPoint& pt) +{ + return wxGenericFindWindowAtPoint(pt); +} + +#endif // wxUSE_GUI + +#if wxUSE_BASE + +#include + +wxString wxGetOsDescription() +{ + struct utsname name; + uname(&name); + return wxString::Format(_T("Mac OS X (%s %s %s)"), + wxString::FromAscii(name.sysname).c_str(), + wxString::FromAscii(name.release).c_str(), + wxString::FromAscii(name.machine).c_str()); +} + +#ifndef __DARWIN__ +wxString wxGetUserHome (const wxString& user) +{ + // TODO + return wxString(); +} + +bool wxGetDiskSpace(const wxString& path, wxDiskspaceSize_t *pTotal, wxDiskspaceSize_t *pFree) +{ + if ( path.empty() ) + return false; + + wxString p = path; + if (p[0u] == ':' ) + p = wxGetCwd() + p; + + int pos = p.Find(':'); + if ( pos != wxNOT_FOUND ) + p = p.Mid(1,pos); + + p = p + wxT(":"); + + OSErr err = noErr; + + FSRef fsRef; + err = wxMacPathToFSRef( p , &fsRef ); + if ( noErr == err ) + { + FSVolumeRefNum vRefNum; + err = FSGetVRefNum( &fsRef , &vRefNum ); + if ( noErr == err ) + { + UInt64 freeBytes , totalBytes; + err = FSGetVInfo( vRefNum , NULL , &freeBytes , &totalBytes ); + if ( noErr == err ) + { + if ( pTotal ) + *pTotal = wxDiskspaceSize_t( totalBytes ); + if ( pFree ) + *pFree = wxDiskspaceSize_t( freeBytes ); + } + } + } + + return err == noErr; +} +#endif // !__DARWIN__ + +//--------------------------------------------------------------------------- +// wxMac Specific utility functions +//--------------------------------------------------------------------------- + +void wxMacStringToPascal( const wxString&from , StringPtr to ) +{ + wxCharBuffer buf = from.mb_str( wxConvLocal ); + int len = strlen(buf); + + if ( len > 255 ) + len = 255; + to[0] = len; + memcpy( (char*) &to[1] , buf , len ); +} + +wxString wxMacMakeStringFromPascal( ConstStringPtr from ) +{ + return wxString( (char*) &from[1] , wxConvLocal , from[0] ); +} + +// ---------------------------------------------------------------------------- +// Common Event Support +// ---------------------------------------------------------------------------- + +void wxMacWakeUp() +{ + OSStatus err = noErr; + +#if 0 + // lead sometimes to race conditions, although all calls used should be thread safe ... + static wxMacCarbonEvent s_wakeupEvent; + if ( !s_wakeupEvent.IsValid() ) + { + err = s_wakeupEvent.Create( 'WXMC', 'WXMC', GetCurrentEventTime(), + kEventAttributeNone ); + } + if ( err == noErr ) + { + + if ( IsEventInQueue( GetMainEventQueue() , s_wakeupEvent ) ) + return; + s_wakeupEvent.SetCurrentTime(); + err = PostEventToQueue(GetMainEventQueue(), s_wakeupEvent, + kEventPriorityHigh ); + } +#else + wxMacCarbonEvent wakeupEvent; + wakeupEvent.Create( 'WXMC', 'WXMC', GetCurrentEventTime(), + kEventAttributeNone ); + err = PostEventToQueue(GetMainEventQueue(), wakeupEvent, + kEventPriorityHigh ); +#endif +} + +#endif // wxUSE_BASE + +#if wxUSE_GUI + +// ---------------------------------------------------------------------------- +// Native Struct Conversions +// ---------------------------------------------------------------------------- + +void wxMacRectToNative( const wxRect *wx , Rect *n ) +{ + n->left = wx->x; + n->top = wx->y; + n->right = wx->x + wx->width; + n->bottom = wx->y + wx->height; +} + +void wxMacNativeToRect( const Rect *n , wxRect* wx ) +{ + wx->x = n->left; + wx->y = n->top; + wx->width = n->right - n->left; + wx->height = n->bottom - n->top; +} + +void wxMacPointToNative( const wxPoint* wx , Point *n ) +{ + n->h = wx->x; + n->v = wx->y; +} + +void wxMacNativeToPoint( const Point *n , wxPoint* wx ) +{ + wx->x = n->h; + wx->y = n->v; +} + +// ---------------------------------------------------------------------------- +// Carbon Event Support +// ---------------------------------------------------------------------------- + +OSStatus wxMacCarbonEvent::GetParameter(EventParamName inName, EventParamType inDesiredType, UInt32 inBufferSize, void * outData) +{ + return ::GetEventParameter( m_eventRef , inName , inDesiredType , NULL , inBufferSize , NULL , outData ); +} + +OSStatus wxMacCarbonEvent::SetParameter(EventParamName inName, EventParamType inType, UInt32 inBufferSize, const void * inData) +{ + return ::SetEventParameter( m_eventRef , inName , inType , inBufferSize , inData ); +} + +// ---------------------------------------------------------------------------- +// Control Access Support +// ---------------------------------------------------------------------------- + +#if wxMAC_USE_QUICKDRAW + +IMPLEMENT_DYNAMIC_CLASS( wxMacControl , wxObject ) + +wxMacControl::wxMacControl() +{ + Init(); +} + +wxMacControl::wxMacControl(wxWindow* peer , bool isRootControl ) +{ + Init(); + m_peer = peer; + m_isRootControl = isRootControl; +} + +wxMacControl::wxMacControl( wxWindow* peer , ControlRef control ) +{ + Init(); + m_peer = peer; + m_controlRef = control; +} + +wxMacControl::wxMacControl( wxWindow* peer , WXWidget control ) +{ + Init(); + m_peer = peer; + m_controlRef = (ControlRef) control; +} + +wxMacControl::~wxMacControl() +{ +} + +void wxMacControl::Init() +{ + m_peer = NULL; + m_controlRef = NULL; + m_needsFocusRect = false; + m_isRootControl = false; +} + +void wxMacControl::Dispose() +{ + wxASSERT_MSG( m_controlRef != NULL , wxT("Control Handle already NULL, Dispose called twice ?") ); + wxASSERT_MSG( IsValidControlHandle(m_controlRef) , wxT("Invalid Control Handle (maybe already released) in Dispose") ); + + // we cannot check the ref count here anymore, as autorelease objects might delete their refs later + // we can have situations when being embedded, where the control gets deleted behind our back, so only + // CFRelease if we are safe + if ( IsValidControlHandle(m_controlRef) ) + CFRelease(m_controlRef); + m_controlRef = NULL; +} + +void wxMacControl::SetReference( URefCon data ) +{ + SetControlReference( m_controlRef , data ); +} + +OSStatus wxMacControl::GetData(ControlPartCode inPartCode , ResType inTag , Size inBufferSize , void * inOutBuffer , Size * outActualSize ) const +{ + return ::GetControlData( m_controlRef , inPartCode , inTag , inBufferSize , inOutBuffer , outActualSize ); +} + +OSStatus wxMacControl::GetDataSize(ControlPartCode inPartCode , ResType inTag , Size * outActualSize ) const +{ + return ::GetControlDataSize( m_controlRef , inPartCode , inTag , outActualSize ); +} + +OSStatus wxMacControl::SetData(ControlPartCode inPartCode , ResType inTag , Size inSize , const void * inData) +{ + return ::SetControlData( m_controlRef , inPartCode , inTag , inSize , inData ); +} + +OSStatus wxMacControl::SendEvent( EventRef event , OptionBits inOptions ) +{ + return SendEventToEventTargetWithOptions( event, + HIObjectGetEventTarget( (HIObjectRef) m_controlRef ), inOptions ); +} + +OSStatus wxMacControl::SendHICommand( HICommand &command , OptionBits inOptions ) +{ + wxMacCarbonEvent event( kEventClassCommand , kEventCommandProcess ); + + event.SetParameter(kEventParamDirectObject,command); + + return SendEvent( event , inOptions ); +} + +OSStatus wxMacControl::SendHICommand( UInt32 commandID , OptionBits inOptions ) +{ + HICommand command; + + memset( &command, 0 , sizeof(command) ); + command.commandID = commandID; + return SendHICommand( command , inOptions ); +} + +void wxMacControl::Flash( ControlPartCode part , UInt32 ticks ) +{ + unsigned long finalTicks; + + HiliteControl( m_controlRef , part ); + Delay( ticks , &finalTicks ); + HiliteControl( m_controlRef , kControlNoPart ); +} + +SInt32 wxMacControl::GetValue() const +{ + return ::GetControl32BitValue( m_controlRef ); +} + +SInt32 wxMacControl::GetMaximum() const +{ + return ::GetControl32BitMaximum( m_controlRef ); +} + +SInt32 wxMacControl::GetMinimum() const +{ + return ::GetControl32BitMinimum( m_controlRef ); +} + +void wxMacControl::SetValue( SInt32 v ) +{ + ::SetControl32BitValue( m_controlRef , v ); +} + +void wxMacControl::SetMinimum( SInt32 v ) +{ + ::SetControl32BitMinimum( m_controlRef , v ); +} + +void wxMacControl::SetMaximum( SInt32 v ) +{ + ::SetControl32BitMaximum( m_controlRef , v ); +} + +void wxMacControl::SetValueAndRange( SInt32 value , SInt32 minimum , SInt32 maximum ) +{ + ::SetControl32BitMinimum( m_controlRef , minimum ); + ::SetControl32BitMaximum( m_controlRef , maximum ); + ::SetControl32BitValue( m_controlRef , value ); +} + +OSStatus wxMacControl::SetFocus( ControlFocusPart focusPart ) +{ + return SetKeyboardFocus( GetControlOwner( m_controlRef ), m_controlRef, focusPart ); +} + +bool wxMacControl::HasFocus() const +{ + ControlRef control; + GetKeyboardFocus( GetUserFocusWindow() , &control ); + return control == m_controlRef; +} + +void wxMacControl::SetNeedsFocusRect( bool needs ) +{ + m_needsFocusRect = needs; +} + +bool wxMacControl::NeedsFocusRect() const +{ + return m_needsFocusRect; +} + +void wxMacControl::VisibilityChanged(bool WXUNUSED(shown)) +{ +} + +void wxMacControl::SuperChangedPosition() +{ +} + +void wxMacControl::SetFont( const wxFont & font , const wxColour& foreground , long windowStyle ) +{ + m_font = font; +#if wxMAC_USE_CORE_TEXT + if ( UMAGetSystemVersion() >= 0x1050 ) + { + HIViewPartCode part = 0; + HIThemeTextHorizontalFlush flush = kHIThemeTextHorizontalFlushDefault; + if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_CENTER_HORIZONTAL ) + flush = kHIThemeTextHorizontalFlushCenter; + else if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_RIGHT ) + flush = kHIThemeTextHorizontalFlushRight; + HIViewSetTextFont( m_controlRef , part , (CTFontRef) font.MacGetCTFont() ); + HIViewSetTextHorizontalFlush( m_controlRef, part, flush ); + + if ( foreground != *wxBLACK ) + { + ControlFontStyleRec fontStyle; + foreground.GetRGBColor( &fontStyle.foreColor ); + fontStyle.flags = kControlUseForeColorMask; + ::SetControlFontStyle( m_controlRef , &fontStyle ); + } + + } +#endif +#if wxMAC_USE_ATSU_TEXT + ControlFontStyleRec fontStyle; + if ( font.MacGetThemeFontID() != kThemeCurrentPortFont ) + { + switch ( font.MacGetThemeFontID() ) + { + case kThemeSmallSystemFont : + fontStyle.font = kControlFontSmallSystemFont; + break; + + case 109 : // mini font + fontStyle.font = -5; + break; + + case kThemeSystemFont : + fontStyle.font = kControlFontBigSystemFont; + break; + + default : + fontStyle.font = kControlFontBigSystemFont; + break; + } + + fontStyle.flags = kControlUseFontMask; + } + else + { + fontStyle.font = font.MacGetFontNum(); + fontStyle.style = font.MacGetFontStyle(); + fontStyle.size = font.MacGetFontSize(); + fontStyle.flags = kControlUseFontMask | kControlUseFaceMask | kControlUseSizeMask; + } + + fontStyle.just = teJustLeft; + fontStyle.flags |= kControlUseJustMask; + if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_CENTER_HORIZONTAL ) + fontStyle.just = teJustCenter; + else if ( ( windowStyle & wxALIGN_MASK ) & wxALIGN_RIGHT ) + fontStyle.just = teJustRight; + + + // we only should do this in case of a non-standard color, as otherwise 'disabled' controls + // won't get grayed out by the system anymore + + if ( foreground != *wxBLACK ) + { + foreground.GetRGBColor( &fontStyle.foreColor ); + fontStyle.flags |= kControlUseForeColorMask; + } + + ::SetControlFontStyle( m_controlRef , &fontStyle ); +#endif +} + +void wxMacControl::SetBackgroundColour( const wxColour &WXUNUSED(col) ) +{ +// HITextViewSetBackgroundColor( m_textView , color ); +} + +void wxMacControl::SetRange( SInt32 minimum , SInt32 maximum ) +{ + ::SetControl32BitMinimum( m_controlRef , minimum ); + ::SetControl32BitMaximum( m_controlRef , maximum ); +} + +short wxMacControl::HandleKey( SInt16 keyCode, SInt16 charCode, EventModifiers modifiers ) +{ +#ifndef __LP64__ + return HandleControlKey( m_controlRef , keyCode , charCode , modifiers ); +#else + return 0; +#endif +} + +void wxMacControl::SetActionProc( ControlActionUPP actionProc ) +{ + SetControlAction( m_controlRef , actionProc ); +} + +void wxMacControl::SetViewSize( SInt32 viewSize ) +{ + SetControlViewSize(m_controlRef , viewSize ); +} + +SInt32 wxMacControl::GetViewSize() const +{ + return GetControlViewSize( m_controlRef ); +} + +bool wxMacControl::IsVisible() const +{ + return IsControlVisible( m_controlRef ); +} + +void wxMacControl::SetVisibility( bool visible , bool redraw ) +{ + SetControlVisibility( m_controlRef , visible , redraw ); +} + +bool wxMacControl::IsEnabled() const +{ + return IsControlEnabled( m_controlRef ); +} + +bool wxMacControl::IsActive() const +{ + return IsControlActive( m_controlRef ); +} + +void wxMacControl::Enable( bool enable ) +{ + if ( enable ) + EnableControl( m_controlRef ); + else + DisableControl( m_controlRef ); +} + +void wxMacControl::SetDrawingEnabled( bool enable ) +{ + HIViewSetDrawingEnabled( m_controlRef , enable ); +} + +bool wxMacControl::GetNeedsDisplay() const +{ + return HIViewGetNeedsDisplay( m_controlRef ); +} + +void wxMacControl::SetNeedsDisplay( RgnHandle where ) +{ + if ( !IsVisible() ) + return; + + HIViewSetNeedsDisplayInRegion( m_controlRef , where , true ); +} + +void wxMacControl::SetNeedsDisplay( Rect* where ) +{ + if ( !IsVisible() ) + return; + + if ( where != NULL ) + { + RgnHandle update = NewRgn(); + RectRgn( update , where ); + HIViewSetNeedsDisplayInRegion( m_controlRef , update , true ); + DisposeRgn( update ); + } + else + HIViewSetNeedsDisplay( m_controlRef , true ); +} + +void wxMacControl::Convert( wxPoint *pt , wxMacControl *from , wxMacControl *to ) +{ + HIPoint hiPoint; + + hiPoint.x = pt->x; + hiPoint.y = pt->y; + HIViewConvertPoint( &hiPoint , from->m_controlRef , to->m_controlRef ); + pt->x = (int)hiPoint.x; + pt->y = (int)hiPoint.y; +} + +void wxMacControl::SetRect( Rect *r ) +{ + //A HIRect is actually a CGRect on OSX - which consists of two structures - + //CGPoint and CGSize, which have two floats each + HIRect hir = { { r->left , r->top }, { r->right - r->left , r->bottom - r->top } }; + HIViewSetFrame ( m_controlRef , &hir ); + // eventuall we might have to do a SetVisibility( false , true ); + // before and a SetVisibility( true , true ); after +} + +void wxMacControl::GetRect( Rect *r ) +{ + GetControlBounds( m_controlRef , r ); +} + +void wxMacControl::GetRectInWindowCoords( Rect *r ) +{ + GetControlBounds( m_controlRef , r ) ; + + WindowRef tlwref = GetControlOwner( m_controlRef ) ; + + wxNonOwnedWindow* tlwwx = wxFindWinFromMacWindow( tlwref ) ; + if ( tlwwx != NULL ) + { + ControlRef rootControl = tlwwx->GetPeer()->GetControlRef() ; + HIPoint hiPoint = CGPointMake( 0 , 0 ) ; + HIViewConvertPoint( &hiPoint , HIViewGetSuperview(m_controlRef) , rootControl ) ; + OffsetRect( r , (short) hiPoint.x , (short) hiPoint.y ) ; + } +} + +void wxMacControl::GetBestRect( Rect *r ) +{ + short baselineoffset; + + GetBestControlRect( m_controlRef , r , &baselineoffset ); +} + +void wxMacControl::SetLabel( const wxString &title ) +{ + wxFontEncoding encoding; + + if ( m_font.Ok() ) + encoding = m_font.GetEncoding(); + else + encoding = wxFont::GetDefaultEncoding(); + + SetControlTitleWithCFString( m_controlRef , wxCFStringRef( title , encoding ) ); +} + +void wxMacControl::GetFeatures( UInt32 * features ) +{ + GetControlFeatures( m_controlRef , features ); +} + +OSStatus wxMacControl::GetRegion( ControlPartCode partCode , RgnHandle region ) +{ + OSStatus err = GetControlRegion( m_controlRef , partCode , region ); + return err; +} + +OSStatus wxMacControl::SetZOrder( bool above , wxMacControl* other ) +{ + return HIViewSetZOrder( m_controlRef,above ? kHIViewZOrderAbove : kHIViewZOrderBelow, + (other != NULL) ? other->m_controlRef : NULL); +} + +// SetNeedsDisplay would not invalidate the children +static void InvalidateControlAndChildren( HIViewRef control ) +{ + HIViewSetNeedsDisplay( control , true ); + UInt16 childrenCount = 0; + OSStatus err = CountSubControls( control , &childrenCount ); + if ( err == errControlIsNotEmbedder ) + return; + + wxASSERT_MSG( err == noErr , wxT("Unexpected error when accessing subcontrols") ); + + for ( UInt16 i = childrenCount; i >=1; --i ) + { + HIViewRef child; + + err = GetIndexedSubControl( control , i , & child ); + if ( err == errControlIsNotEmbedder ) + return; + + InvalidateControlAndChildren( child ); + } +} + +void wxMacControl::InvalidateWithChildren() +{ + InvalidateControlAndChildren( m_controlRef ); +} + +void wxMacControl::ScrollRect( wxRect *r , int dx , int dy ) +{ + wxASSERT( r != NULL ); + + HIRect scrollarea = CGRectMake( r->x , r->y , r->width , r->height); + HIViewScrollRect ( m_controlRef , &scrollarea , dx ,dy ); +} + +OSType wxMacCreator = 'WXMC'; +OSType wxMacControlProperty = 'MCCT'; + +void wxMacControl::SetReferenceInNativeControl() +{ + void * data = this; + verify_noerr( SetControlProperty ( m_controlRef , + wxMacCreator,wxMacControlProperty, sizeof(data), &data ) ); +} + +wxMacControl* wxMacControl::GetReferenceFromNativeControl(ControlRef control) +{ + wxMacControl* ctl = NULL; + ByteCount actualSize; + if ( GetControlProperty( control ,wxMacCreator,wxMacControlProperty, sizeof(ctl) , + &actualSize , &ctl ) == noErr ) + { + return ctl; + } + return NULL; +} + +// ============================================================================ +// DataBrowser Wrapper +// ============================================================================ +// +// basing on DataBrowserItemIDs +// + +IMPLEMENT_ABSTRACT_CLASS( wxMacDataBrowserControl , wxMacControl ) + +pascal void wxMacDataBrowserControl::DataBrowserItemNotificationProc( + ControlRef browser, + DataBrowserItemID itemID, + DataBrowserItemNotification message, + DataBrowserItemDataRef itemData ) +{ + wxMacDataBrowserControl* ctl = wxDynamicCast(wxMacControl::GetReferenceFromNativeControl( browser ), wxMacDataBrowserControl); + if ( ctl != 0 ) + { + ctl->ItemNotification(itemID, message, itemData); + } +} + +pascal OSStatus wxMacDataBrowserControl::DataBrowserGetSetItemDataProc( + ControlRef browser, + DataBrowserItemID itemID, + DataBrowserPropertyID property, + DataBrowserItemDataRef itemData, + Boolean changeValue ) +{ + OSStatus err = errDataBrowserPropertyNotSupported; + wxMacDataBrowserControl* ctl = wxDynamicCast(wxMacControl::GetReferenceFromNativeControl( browser ), wxMacDataBrowserControl); + if ( ctl != 0 ) + { + err = ctl->GetSetItemData(itemID, property, itemData, changeValue); + } + return err; +} + +pascal Boolean wxMacDataBrowserControl::DataBrowserCompareProc( + ControlRef browser, + DataBrowserItemID itemOneID, + DataBrowserItemID itemTwoID, + DataBrowserPropertyID sortProperty) +{ + wxMacDataBrowserControl* ctl = wxDynamicCast(wxMacControl::GetReferenceFromNativeControl( browser ), wxMacDataBrowserControl); + if ( ctl != 0 ) + { + return ctl->CompareItems(itemOneID, itemTwoID, sortProperty); + } + return false; +} + +DataBrowserItemDataUPP gDataBrowserItemDataUPP = NULL; +DataBrowserItemNotificationUPP gDataBrowserItemNotificationUPP = NULL; +DataBrowserItemCompareUPP gDataBrowserItemCompareUPP = NULL; + +wxMacDataBrowserControl::wxMacDataBrowserControl( wxWindow* peer, + const wxPoint& pos, + const wxSize& size, + long WXUNUSED(style)) + : wxMacControl( peer ) +{ + Rect bounds = wxMacGetBoundsForControl( peer, pos, size ); + OSStatus err = ::CreateDataBrowserControl( + MAC_WXHWND(peer->MacGetTopLevelWindowRef()), + &bounds, kDataBrowserListView, &m_controlRef ); + SetReferenceInNativeControl(); + verify_noerr( err ); + if ( gDataBrowserItemCompareUPP == NULL ) + gDataBrowserItemCompareUPP = NewDataBrowserItemCompareUPP(DataBrowserCompareProc); + if ( gDataBrowserItemDataUPP == NULL ) + gDataBrowserItemDataUPP = NewDataBrowserItemDataUPP(DataBrowserGetSetItemDataProc); + if ( gDataBrowserItemNotificationUPP == NULL ) + { + gDataBrowserItemNotificationUPP = + (DataBrowserItemNotificationUPP) NewDataBrowserItemNotificationWithItemUPP(DataBrowserItemNotificationProc); + } + + DataBrowserCallbacks callbacks; + InitializeDataBrowserCallbacks( &callbacks, kDataBrowserLatestCallbacks ); + + callbacks.u.v1.itemDataCallback = gDataBrowserItemDataUPP; + callbacks.u.v1.itemCompareCallback = gDataBrowserItemCompareUPP; + callbacks.u.v1.itemNotificationCallback = gDataBrowserItemNotificationUPP; + SetCallbacks( &callbacks ); + +} + +OSStatus wxMacDataBrowserControl::GetItemCount( DataBrowserItemID container, + Boolean recurse, + DataBrowserItemState state, + ItemCount *numItems) const +{ + return GetDataBrowserItemCount( m_controlRef, container, recurse, state, numItems ); +} + +OSStatus wxMacDataBrowserControl::GetItems( DataBrowserItemID container, + Boolean recurse, + DataBrowserItemState state, + Handle items) const +{ + return GetDataBrowserItems( m_controlRef, container, recurse, state, items ); +} + +OSStatus wxMacDataBrowserControl::SetSelectionFlags( DataBrowserSelectionFlags options ) +{ + return SetDataBrowserSelectionFlags( m_controlRef, options ); +} + +OSStatus wxMacDataBrowserControl::AddColumn( DataBrowserListViewColumnDesc *columnDesc, + DataBrowserTableViewColumnIndex position ) +{ + return AddDataBrowserListViewColumn( m_controlRef, columnDesc, position ); +} + +OSStatus wxMacDataBrowserControl::GetColumnIDFromIndex( DataBrowserTableViewColumnIndex position, DataBrowserTableViewColumnID* id ){ + return GetDataBrowserTableViewColumnProperty( m_controlRef, position, id ); +} + +OSStatus wxMacDataBrowserControl::RemoveColumn( DataBrowserTableViewColumnIndex position ) +{ + DataBrowserTableViewColumnID id; + GetColumnIDFromIndex( position, &id ); + return RemoveDataBrowserTableViewColumn( m_controlRef, id ); +} + +OSStatus wxMacDataBrowserControl::AutoSizeColumns() +{ + return AutoSizeDataBrowserListViewColumns(m_controlRef); +} + +OSStatus wxMacDataBrowserControl::SetHasScrollBars( bool horiz, bool vert ) +{ + return SetDataBrowserHasScrollBars( m_controlRef, horiz, vert ); +} + +OSStatus wxMacDataBrowserControl::SetHiliteStyle( DataBrowserTableViewHiliteStyle hiliteStyle ) +{ + return SetDataBrowserTableViewHiliteStyle( m_controlRef, hiliteStyle ); +} + +OSStatus wxMacDataBrowserControl::SetHeaderButtonHeight(UInt16 height) +{ + return SetDataBrowserListViewHeaderBtnHeight( m_controlRef, height ); +} + +OSStatus wxMacDataBrowserControl::GetHeaderButtonHeight(UInt16 *height) +{ + return GetDataBrowserListViewHeaderBtnHeight( m_controlRef, height ); +} + +OSStatus wxMacDataBrowserControl::SetCallbacks(const DataBrowserCallbacks *callbacks) +{ + return SetDataBrowserCallbacks( m_controlRef, callbacks ); +} + +OSStatus wxMacDataBrowserControl::UpdateItems( + DataBrowserItemID container, + UInt32 numItems, + const DataBrowserItemID *items, + DataBrowserPropertyID preSortProperty, + DataBrowserPropertyID propertyID ) const +{ + return UpdateDataBrowserItems( m_controlRef, container, numItems, items, preSortProperty, propertyID ); +} + +bool wxMacDataBrowserControl::IsItemSelected( DataBrowserItemID item ) const +{ + return IsDataBrowserItemSelected( m_controlRef, item ); +} + +OSStatus wxMacDataBrowserControl::AddItems( + DataBrowserItemID container, + UInt32 numItems, + const DataBrowserItemID *items, + DataBrowserPropertyID preSortProperty ) +{ + return AddDataBrowserItems( m_controlRef, container, numItems, items, preSortProperty ); +} + +OSStatus wxMacDataBrowserControl::RemoveItems( + DataBrowserItemID container, + UInt32 numItems, + const DataBrowserItemID *items, + DataBrowserPropertyID preSortProperty ) +{ + return RemoveDataBrowserItems( m_controlRef, container, numItems, items, preSortProperty ); +} + +OSStatus wxMacDataBrowserControl::RevealItem( + DataBrowserItemID item, + DataBrowserPropertyID propertyID, + DataBrowserRevealOptions options ) const +{ + return RevealDataBrowserItem( m_controlRef, item, propertyID, options ); +} + +OSStatus wxMacDataBrowserControl::SetSelectedItems( + UInt32 numItems, + const DataBrowserItemID *items, + DataBrowserSetOption operation ) +{ + return SetDataBrowserSelectedItems( m_controlRef, numItems, items, operation ); +} + +OSStatus wxMacDataBrowserControl::GetSelectionAnchor( DataBrowserItemID *first, DataBrowserItemID *last ) const +{ + return GetDataBrowserSelectionAnchor( m_controlRef, first, last ); +} + +OSStatus wxMacDataBrowserControl::GetItemID( DataBrowserTableViewRowIndex row, DataBrowserItemID * item ) const +{ + return GetDataBrowserTableViewItemID( m_controlRef, row, item ); +} + +OSStatus wxMacDataBrowserControl::GetItemRow( DataBrowserItemID item, DataBrowserTableViewRowIndex * row ) const +{ + return GetDataBrowserTableViewItemRow( m_controlRef, item, row ); +} + +OSStatus wxMacDataBrowserControl::SetDefaultRowHeight( UInt16 height ) +{ + return SetDataBrowserTableViewRowHeight( m_controlRef , height ); +} + +OSStatus wxMacDataBrowserControl::GetDefaultRowHeight( UInt16 * height ) const +{ + return GetDataBrowserTableViewRowHeight( m_controlRef, height ); +} + +OSStatus wxMacDataBrowserControl::SetRowHeight( DataBrowserItemID item , UInt16 height) +{ + return SetDataBrowserTableViewItemRowHeight( m_controlRef, item , height ); +} + +OSStatus wxMacDataBrowserControl::GetRowHeight( DataBrowserItemID item , UInt16 *height) const +{ + return GetDataBrowserTableViewItemRowHeight( m_controlRef, item , height); +} + +OSStatus wxMacDataBrowserControl::GetColumnWidth( DataBrowserPropertyID column , UInt16 *width ) const +{ + return GetDataBrowserTableViewNamedColumnWidth( m_controlRef , column , width ); +} + +OSStatus wxMacDataBrowserControl::SetColumnWidth( DataBrowserPropertyID column , UInt16 width ) +{ + return SetDataBrowserTableViewNamedColumnWidth( m_controlRef , column , width ); +} + +OSStatus wxMacDataBrowserControl::GetDefaultColumnWidth( UInt16 *width ) const +{ + return GetDataBrowserTableViewColumnWidth( m_controlRef , width ); +} + +OSStatus wxMacDataBrowserControl::SetDefaultColumnWidth( UInt16 width ) +{ + return SetDataBrowserTableViewColumnWidth( m_controlRef , width ); +} + +OSStatus wxMacDataBrowserControl::GetColumnCount(UInt32* numColumns) const +{ + return GetDataBrowserTableViewColumnCount( m_controlRef, numColumns); +} + +OSStatus wxMacDataBrowserControl::GetColumnPosition( DataBrowserPropertyID column, + DataBrowserTableViewColumnIndex *position) const +{ + return GetDataBrowserTableViewColumnPosition( m_controlRef , column , position); +} + +OSStatus wxMacDataBrowserControl::SetColumnPosition( DataBrowserPropertyID column, DataBrowserTableViewColumnIndex position) +{ + return SetDataBrowserTableViewColumnPosition( m_controlRef , column , position); +} + +OSStatus wxMacDataBrowserControl::GetScrollPosition( UInt32 *top , UInt32 *left ) const +{ + return GetDataBrowserScrollPosition( m_controlRef , top , left ); +} + +OSStatus wxMacDataBrowserControl::SetScrollPosition( UInt32 top , UInt32 left ) +{ + return SetDataBrowserScrollPosition( m_controlRef , top , left ); +} + +OSStatus wxMacDataBrowserControl::GetSortProperty( DataBrowserPropertyID *column ) const +{ + return GetDataBrowserSortProperty( m_controlRef , column ); +} + +OSStatus wxMacDataBrowserControl::SetSortProperty( DataBrowserPropertyID column ) +{ + return SetDataBrowserSortProperty( m_controlRef , column ); +} + +OSStatus wxMacDataBrowserControl::GetSortOrder( DataBrowserSortOrder *order ) const +{ + return GetDataBrowserSortOrder( m_controlRef , order ); +} + +OSStatus wxMacDataBrowserControl::SetSortOrder( DataBrowserSortOrder order ) +{ + return SetDataBrowserSortOrder( m_controlRef , order ); +} + +OSStatus wxMacDataBrowserControl::GetPropertyFlags( DataBrowserPropertyID property, + DataBrowserPropertyFlags *flags ) const +{ + return GetDataBrowserPropertyFlags( m_controlRef , property , flags ); +} + +OSStatus wxMacDataBrowserControl::SetPropertyFlags( DataBrowserPropertyID property, + DataBrowserPropertyFlags flags ) +{ + return SetDataBrowserPropertyFlags( m_controlRef , property , flags ); +} + +OSStatus wxMacDataBrowserControl::GetHeaderDesc( DataBrowserPropertyID property, + DataBrowserListViewHeaderDesc *desc ) const +{ + return GetDataBrowserListViewHeaderDesc( m_controlRef , property , desc ); +} + +OSStatus wxMacDataBrowserControl::SetHeaderDesc( DataBrowserPropertyID property, + DataBrowserListViewHeaderDesc *desc ) +{ + return SetDataBrowserListViewHeaderDesc( m_controlRef , property , desc ); +} + +OSStatus wxMacDataBrowserControl::SetDisclosureColumn( DataBrowserPropertyID property , + Boolean expandableRows ) +{ + return SetDataBrowserListViewDisclosureColumn( m_controlRef, property, expandableRows); +} + +// ============================================================================ +// Higher-level Databrowser +// ============================================================================ +// +// basing on data item objects +// + +wxMacDataItem::wxMacDataItem() +{ + m_data = NULL; + + m_order = 0; + m_colId = kTextColumnId; // for compat with existing wx*ListBox impls. +} + +wxMacDataItem::~wxMacDataItem() +{ +} + +void wxMacDataItem::SetOrder( SInt32 order ) +{ + m_order = order; +} + +SInt32 wxMacDataItem::GetOrder() const +{ + return m_order; +} + +void wxMacDataItem::SetData( void* data) +{ + m_data = data; +} + +void* wxMacDataItem::GetData() const +{ + return m_data; +} + +short wxMacDataItem::GetColumn() +{ + return m_colId; +} + +void wxMacDataItem::SetColumn( short col ) +{ + m_colId = col; +} + +void wxMacDataItem::SetLabel( const wxString& str) +{ + m_label = str; + m_cfLabel = wxCFStringRef( str , wxLocale::GetSystemEncoding()); +} + +const wxString& wxMacDataItem::GetLabel() const +{ + return m_label; +} + +bool wxMacDataItem::IsLessThan(wxMacDataItemBrowserControl *WXUNUSED(owner) , + const wxMacDataItem* rhs, + DataBrowserPropertyID sortProperty) const +{ + const wxMacDataItem* otherItem = wx_const_cast(wxMacDataItem*,rhs); + bool retval = false; + + if ( sortProperty == m_colId ){ + retval = m_label.CmpNoCase( otherItem->m_label) < 0; + } + + else if ( sortProperty == kNumericOrderColumnId ) + retval = m_order < otherItem->m_order; + + return retval; +} + +OSStatus wxMacDataItem::GetSetData( wxMacDataItemBrowserControl *WXUNUSED(owner) , + DataBrowserPropertyID property, + DataBrowserItemDataRef itemData, + bool changeValue ) +{ + OSStatus err = errDataBrowserPropertyNotSupported; + if ( !changeValue ) + { + if ( property == m_colId ){ + err = ::SetDataBrowserItemDataText( itemData, m_cfLabel ); + err = noErr; + } + else if ( property == kNumericOrderColumnId ){ + err = ::SetDataBrowserItemDataValue( itemData, m_order ); + err = noErr; + } + else{ + } + } + else + { + switch (property) + { + // no editable props here + default: + break; + } + } + + return err; +} + +void wxMacDataItem::Notification(wxMacDataItemBrowserControl *WXUNUSED(owner) , + DataBrowserItemNotification WXUNUSED(message), + DataBrowserItemDataRef WXUNUSED(itemData) ) const +{ +} + +IMPLEMENT_DYNAMIC_CLASS( wxMacDataItemBrowserControl , wxMacDataBrowserControl ) + +wxMacDataItemBrowserControl::wxMacDataItemBrowserControl( wxWindow* peer , const wxPoint& pos, const wxSize& size, long style) : + wxMacDataBrowserControl( peer, pos, size, style ) +{ + m_suppressSelection = false; + m_sortOrder = SortOrder_None; + m_clientDataItemsType = wxClientData_None; +} + +wxMacDataItem* wxMacDataItemBrowserControl::CreateItem() +{ + return new wxMacDataItem(); +} + +wxMacDataItemBrowserSelectionSuppressor::wxMacDataItemBrowserSelectionSuppressor(wxMacDataItemBrowserControl *browser) +{ + m_former = browser->SuppressSelection(true); + m_browser = browser; +} + +wxMacDataItemBrowserSelectionSuppressor::~wxMacDataItemBrowserSelectionSuppressor() +{ + m_browser->SuppressSelection(m_former); +} + +bool wxMacDataItemBrowserControl::SuppressSelection( bool suppress ) +{ + bool former = m_suppressSelection; + m_suppressSelection = suppress; + + return former; +} + +Boolean wxMacDataItemBrowserControl::CompareItems(DataBrowserItemID itemOneID, + DataBrowserItemID itemTwoID, + DataBrowserPropertyID sortProperty) +{ + wxMacDataItem* itemOne = (wxMacDataItem*) itemOneID; + wxMacDataItem* itemTwo = (wxMacDataItem*) itemTwoID; + return CompareItems( itemOne , itemTwo , sortProperty ); +} + +Boolean wxMacDataItemBrowserControl::CompareItems(const wxMacDataItem* itemOne, + const wxMacDataItem* itemTwo, + DataBrowserPropertyID sortProperty) +{ + Boolean retval = false; + if ( itemOne != NULL ) + retval = itemOne->IsLessThan( this , itemTwo , sortProperty); + return retval; +} + +OSStatus wxMacDataItemBrowserControl::GetSetItemData( + DataBrowserItemID itemID, + DataBrowserPropertyID property, + DataBrowserItemDataRef itemData, + Boolean changeValue ) +{ + wxMacDataItem* item = (wxMacDataItem*) itemID; + return GetSetItemData(item, property, itemData , changeValue ); +} + +OSStatus wxMacDataItemBrowserControl::GetSetItemData( + wxMacDataItem* item, + DataBrowserPropertyID property, + DataBrowserItemDataRef itemData, + Boolean changeValue ) +{ + OSStatus err = errDataBrowserPropertyNotSupported; + switch( property ) + { + case kDataBrowserContainerIsClosableProperty : + case kDataBrowserContainerIsSortableProperty : + case kDataBrowserContainerIsOpenableProperty : + // right now default behaviour on these + break; + default : + + if ( item != NULL ){ + err = item->GetSetData( this, property , itemData , changeValue ); + } + break; + + } + return err; +} + +void wxMacDataItemBrowserControl::ItemNotification( + DataBrowserItemID itemID, + DataBrowserItemNotification message, + DataBrowserItemDataRef itemData) +{ + wxMacDataItem* item = (wxMacDataItem*) itemID; + ItemNotification( item , message, itemData); +} + +void wxMacDataItemBrowserControl::ItemNotification( + const wxMacDataItem* item, + DataBrowserItemNotification message, + DataBrowserItemDataRef itemData) +{ + if (item != NULL) + item->Notification( this, message, itemData); +} + +unsigned int wxMacDataItemBrowserControl::GetItemCount(const wxMacDataItem* container, + bool recurse , DataBrowserItemState state) const +{ + ItemCount numItems = 0; + verify_noerr( wxMacDataBrowserControl::GetItemCount( (DataBrowserItemID)container, + recurse, state, &numItems ) ); + return numItems; +} + +unsigned int wxMacDataItemBrowserControl::GetSelectedItemCount( const wxMacDataItem* container, + bool recurse ) const +{ + return GetItemCount( container, recurse, kDataBrowserItemIsSelected ); + +} + +void wxMacDataItemBrowserControl::GetItems(const wxMacDataItem* container, + bool recurse , DataBrowserItemState state, wxArrayMacDataItemPtr &items) const +{ + Handle handle = NewHandle(0); + verify_noerr( wxMacDataBrowserControl::GetItems( (DataBrowserItemID)container , + recurse , state, handle) ); + + int itemCount = GetHandleSize(handle)/sizeof(DataBrowserItemID); + HLock( handle ); + wxMacDataItemPtr* itemsArray = (wxMacDataItemPtr*) *handle; + for ( int i = 0; i < itemCount; ++i) + { + items.Add(itemsArray[i]); + } + HUnlock( handle ); + DisposeHandle( handle ); +} + +unsigned int wxMacDataItemBrowserControl::GetLineFromItem(const wxMacDataItem* item) const +{ + DataBrowserTableViewRowIndex row; + OSStatus err = GetItemRow( (DataBrowserItemID) item , &row); + wxCHECK( err == noErr, (unsigned)-1 ); + return row; +} + +wxMacDataItem* wxMacDataItemBrowserControl::GetItemFromLine(unsigned int n) const +{ + DataBrowserItemID id; + OSStatus err = GetItemID( (DataBrowserTableViewRowIndex) n , &id); + wxCHECK( err == noErr, NULL ); + return (wxMacDataItem*) id; +} + +void wxMacDataItemBrowserControl::UpdateItem(const wxMacDataItem *container, + const wxMacDataItem *item , DataBrowserPropertyID property) const +{ + verify_noerr( wxMacDataBrowserControl::UpdateItems((DataBrowserItemID)container, 1, + (DataBrowserItemID*) &item, kDataBrowserItemNoProperty /* notSorted */, property ) ); +} + +void wxMacDataItemBrowserControl::UpdateItems(const wxMacDataItem *container, + wxArrayMacDataItemPtr &itemArray , DataBrowserPropertyID property) const +{ + unsigned int noItems = itemArray.GetCount(); + DataBrowserItemID *items = new DataBrowserItemID[noItems]; + for ( unsigned int i = 0; i < noItems; ++i ) + items[i] = (DataBrowserItemID) itemArray[i]; + + verify_noerr( wxMacDataBrowserControl::UpdateItems((DataBrowserItemID)container, noItems, + items, kDataBrowserItemNoProperty /* notSorted */, property ) ); + delete [] items; +} + +void wxMacDataItemBrowserControl::InsertColumn(int colId, DataBrowserPropertyType colType, + const wxString& title, SInt16 just, int defaultWidth) +{ + DataBrowserListViewColumnDesc columnDesc; + columnDesc.headerBtnDesc.titleOffset = 0; + columnDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc; + + columnDesc.headerBtnDesc.btnFontStyle.flags = + kControlUseFontMask | kControlUseJustMask; + + columnDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly; + columnDesc.headerBtnDesc.btnFontStyle.just = just; + columnDesc.headerBtnDesc.btnFontStyle.font = kControlFontViewSystemFont; + columnDesc.headerBtnDesc.btnFontStyle.style = normal; + + // TODO: Why is m_font not defined when we enter wxLC_LIST mode, but is + // defined for other modes? + wxFontEncoding enc; + if ( m_font.Ok() ) + enc = m_font.GetEncoding(); + else + enc = wxLocale::GetSystemEncoding(); + wxCFStringRef cfTitle( title, enc ); + columnDesc.headerBtnDesc.titleString = cfTitle; + + columnDesc.headerBtnDesc.minimumWidth = 0; + columnDesc.headerBtnDesc.maximumWidth = 30000; + + columnDesc.propertyDesc.propertyID = (kMinColumnId + colId); + columnDesc.propertyDesc.propertyType = colType; + columnDesc.propertyDesc.propertyFlags = kDataBrowserListViewSortableColumn; + columnDesc.propertyDesc.propertyFlags |= kDataBrowserListViewTypeSelectColumn; + columnDesc.propertyDesc.propertyFlags |= kDataBrowserListViewNoGapForIconInHeaderButton; + + verify_noerr( AddColumn( &columnDesc, kDataBrowserListViewAppendColumn ) ); + + if (defaultWidth > 0){ + SetColumnWidth(colId, defaultWidth); + } + +} + +void wxMacDataItemBrowserControl::SetColumnWidth(int colId, int width) +{ + DataBrowserPropertyID id; + GetColumnIDFromIndex(colId, &id); + verify_noerr( wxMacDataBrowserControl::SetColumnWidth(id, width)); +} + +int wxMacDataItemBrowserControl::GetColumnWidth(int colId) +{ + DataBrowserPropertyID id; + GetColumnIDFromIndex(colId, &id); + UInt16 result; + verify_noerr( wxMacDataBrowserControl::GetColumnWidth(id, &result)); + return result; +} + +void wxMacDataItemBrowserControl::AddItem(wxMacDataItem *container, wxMacDataItem *item) +{ + verify_noerr( wxMacDataBrowserControl::AddItems( (DataBrowserItemID)container, 1, + (DataBrowserItemID*) &item, kDataBrowserItemNoProperty ) ); +} + +void wxMacDataItemBrowserControl::AddItems(wxMacDataItem *container, wxArrayMacDataItemPtr &itemArray ) +{ + unsigned int noItems = itemArray.GetCount(); + DataBrowserItemID *items = new DataBrowserItemID[noItems]; + for ( unsigned int i = 0; i < noItems; ++i ) + items[i] = (DataBrowserItemID) itemArray[i]; + + verify_noerr( wxMacDataBrowserControl::AddItems( (DataBrowserItemID)container, noItems, + (DataBrowserItemID*) items, kDataBrowserItemNoProperty ) ); + delete [] items; +} + +void wxMacDataItemBrowserControl::RemoveItem(wxMacDataItem *container, wxMacDataItem* item) +{ + OSStatus err = wxMacDataBrowserControl::RemoveItems( (DataBrowserItemID)container, 1, + (DataBrowserItemID*) &item, kDataBrowserItemNoProperty ); + verify_noerr( err ); +} + +void wxMacDataItemBrowserControl::RemoveItems(wxMacDataItem *container, wxArrayMacDataItemPtr &itemArray) +{ + unsigned int noItems = itemArray.GetCount(); + DataBrowserItemID *items = new DataBrowserItemID[noItems]; + for ( unsigned int i = 0; i < noItems; ++i ) + items[i] = (DataBrowserItemID) itemArray[i]; + + OSStatus err = wxMacDataBrowserControl::RemoveItems( (DataBrowserItemID)container, noItems, + (DataBrowserItemID*) items, kDataBrowserItemNoProperty ); + verify_noerr( err ); + delete [] items; +} + +void wxMacDataItemBrowserControl::RemoveAllItems(wxMacDataItem *container) +{ + OSStatus err = wxMacDataBrowserControl::RemoveItems( (DataBrowserItemID)container, 0 , NULL , kDataBrowserItemNoProperty ); + verify_noerr( err ); +} + +void wxMacDataItemBrowserControl::SetSelectedItem(wxMacDataItem* item , DataBrowserSetOption option) +{ + verify_noerr(wxMacDataBrowserControl::SetSelectedItems( 1, (DataBrowserItemID*) &item, option )); +} + +void wxMacDataItemBrowserControl::SetSelectedAllItems(DataBrowserSetOption option) +{ + verify_noerr(wxMacDataBrowserControl::SetSelectedItems( 0 , NULL , option )); +} + +void wxMacDataItemBrowserControl::SetSelectedItems(wxArrayMacDataItemPtr &itemArray , DataBrowserSetOption option) +{ + unsigned int noItems = itemArray.GetCount(); + DataBrowserItemID *items = new DataBrowserItemID[noItems]; + for ( unsigned int i = 0; i < noItems; ++i ) + items[i] = (DataBrowserItemID) itemArray[i]; + + verify_noerr(wxMacDataBrowserControl::SetSelectedItems( noItems, (DataBrowserItemID*) items, option )); + delete [] items; +} + +Boolean wxMacDataItemBrowserControl::IsItemSelected( const wxMacDataItem* item) const +{ + return wxMacDataBrowserControl::IsItemSelected( (DataBrowserItemID) item); +} + +void wxMacDataItemBrowserControl::RevealItem( wxMacDataItem* item, DataBrowserRevealOptions options) +{ + verify_noerr(wxMacDataBrowserControl::RevealItem( (DataBrowserItemID) item, kDataBrowserNoItem , options ) ); +} + +void wxMacDataItemBrowserControl::GetSelectionAnchor( wxMacDataItemPtr* first , wxMacDataItemPtr* last) const +{ + verify_noerr(wxMacDataBrowserControl::GetSelectionAnchor( (DataBrowserItemID*) first, (DataBrowserItemID*) last) ); +} + +wxClientDataType wxMacDataItemBrowserControl::GetClientDataType() const +{ + return m_clientDataItemsType; +} +void wxMacDataItemBrowserControl::SetClientDataType(wxClientDataType clientDataItemsType) +{ + m_clientDataItemsType = clientDataItemsType; +} + +unsigned int wxMacDataItemBrowserControl::MacGetCount() const +{ + return GetItemCount(wxMacDataBrowserRootContainer,false,kDataBrowserItemAnyState); +} + +void wxMacDataItemBrowserControl::MacDelete( unsigned int n ) +{ + wxMacDataItem* item = (wxMacDataItem*)GetItemFromLine( n ); + RemoveItem( wxMacDataBrowserRootContainer, item ); +} + +void wxMacDataItemBrowserControl::MacInsert( unsigned int n, + const wxArrayStringsAdapter& items, + int column ) +{ + size_t itemsCount = items.GetCount(); + if ( itemsCount == 0 ) + return; + + SInt32 frontLineOrder = 0; + + if ( m_sortOrder == SortOrder_None ) + { + // increase the order of the lines to be shifted + unsigned int lines = MacGetCount(); + for ( unsigned int i = n; i < lines; ++i) + { + wxMacDataItem* iter = (wxMacDataItem*) GetItemFromLine(i); + iter->SetOrder( iter->GetOrder() + itemsCount ); + } + if ( n > 0 ) + { + wxMacDataItem* iter = (wxMacDataItem*) GetItemFromLine(n-1); + frontLineOrder = iter->GetOrder(); + } + } + + wxArrayMacDataItemPtr ids; + ids.SetCount( itemsCount ); + + for ( unsigned int i = 0; i < itemsCount; ++i ) + { + wxMacDataItem* item = CreateItem(); + item->SetLabel( items[i]); + if ( column != -1 ) + item->SetColumn( kMinColumnId + column ); + + if ( m_sortOrder == SortOrder_None ) + item->SetOrder( frontLineOrder + 1 + i ); + + ids[i] = item; + } + + AddItems( wxMacDataBrowserRootContainer, ids ); +} + +int wxMacDataItemBrowserControl::MacAppend( const wxString& text) +{ + wxMacDataItem* item = CreateItem(); + item->SetLabel( text ); + if ( m_sortOrder == SortOrder_None ) + { + unsigned int lines = MacGetCount(); + if ( lines == 0 ) + item->SetOrder( 1 ); + else + { + wxMacDataItem* frontItem = (wxMacDataItem*) GetItemFromLine(lines-1); + item->SetOrder( frontItem->GetOrder() + 1 ); + } + } + AddItem( wxMacDataBrowserRootContainer, item ); + + return GetLineFromItem(item); +} + +void wxMacDataItemBrowserControl::MacClear() +{ + wxMacDataItemBrowserSelectionSuppressor suppressor(this); + RemoveAllItems(wxMacDataBrowserRootContainer); +} + +void wxMacDataItemBrowserControl::MacDeselectAll() +{ + wxMacDataItemBrowserSelectionSuppressor suppressor(this); + SetSelectedAllItems( kDataBrowserItemsRemove ); +} + +void wxMacDataItemBrowserControl::MacSetSelection( unsigned int n, bool select, bool multi ) +{ + wxMacDataItem* item = (wxMacDataItem*) GetItemFromLine(n); + wxMacDataItemBrowserSelectionSuppressor suppressor(this); + + if ( IsItemSelected( item ) != select ) + { + if ( select ) + SetSelectedItem( item, multi ? kDataBrowserItemsAdd : kDataBrowserItemsAssign ); + else + SetSelectedItem( item, kDataBrowserItemsRemove ); + } + + MacScrollTo( n ); +} + +bool wxMacDataItemBrowserControl::MacIsSelected( unsigned int n ) const +{ + wxMacDataItem* item = (wxMacDataItem*) GetItemFromLine(n); + return IsItemSelected( item ); +} + +int wxMacDataItemBrowserControl::MacGetSelection() const +{ + wxMacDataItemPtr first, last; + GetSelectionAnchor( &first, &last ); + + if ( first != NULL ) + { + return GetLineFromItem( first ); + } + + return -1; +} + +int wxMacDataItemBrowserControl::MacGetSelections( wxArrayInt& aSelections ) const +{ + aSelections.Empty(); + wxArrayMacDataItemPtr selectedItems; + GetItems( wxMacDataBrowserRootContainer, false , kDataBrowserItemIsSelected, selectedItems); + + int count = selectedItems.GetCount(); + + for ( int i = 0; i < count; ++i) + { + aSelections.Add(GetLineFromItem(selectedItems[i])); + } + + return count; +} + +void wxMacDataItemBrowserControl::MacSetString( unsigned int n, const wxString& text ) +{ + // as we don't store the strings we only have to issue a redraw + wxMacDataItem* item = (wxMacDataItem*) GetItemFromLine( n); + item->SetLabel( text ); + UpdateItem( wxMacDataBrowserRootContainer, item , kTextColumnId ); +} + +wxString wxMacDataItemBrowserControl::MacGetString( unsigned int n ) const +{ + wxMacDataItem * item = (wxMacDataItem*) GetItemFromLine( n ); + return item->GetLabel(); +} + +void wxMacDataItemBrowserControl::MacSetClientData( unsigned int n, void * data) +{ + wxMacDataItem* item = (wxMacDataItem*) GetItemFromLine( n); + item->SetData( data ); + // not displayed, therefore no Update infos to DataBrowser +} + +void * wxMacDataItemBrowserControl::MacGetClientData( unsigned int n) const +{ + wxMacDataItem * item = (wxMacDataItem*) GetItemFromLine( n ); + return item->GetData(); +} + +void wxMacDataItemBrowserControl::MacScrollTo( unsigned int n ) +{ + UInt32 top , left ; + GetScrollPosition( &top , &left ) ; + wxMacDataItem * item = (wxMacDataItem*) GetItemFromLine( n ); + + // there is a bug in RevealItem that leads to situations + // in large lists, where the item does not get scrolled + // into sight, so we do a pre-scroll if necessary + UInt16 height ; + GetRowHeight( (DataBrowserItemID) item , &height ) ; + UInt32 linetop = n * ((UInt32) height ); + UInt32 linebottom = linetop + height; + Rect rect ; + GetRect( &rect ); + + if ( linetop < top || linebottom > (top + rect.bottom - rect.top ) ) + SetScrollPosition( wxMax( n-2, 0 ) * ((UInt32)height) , left ) ; + + RevealItem( item , kDataBrowserRevealWithoutSelecting ); +} + + + +// +// Tab Control +// + +OSStatus wxMacControl::SetTabEnabled( SInt16 tabNo , bool enable ) +{ + return ::SetTabEnabled( m_controlRef , tabNo , enable ); +} + +#endif + +// +// Quartz Support +// + +/* + Return the generic RGB color space. This is a 'get' function and the caller should + not release the returned value unless the caller retains it first. Usually callers + of this routine will immediately use the returned colorspace with CoreGraphics + so they typically do not need to retain it themselves. + + This function creates the generic RGB color space once and hangs onto it so it can + return it whenever this function is called. +*/ + +CGColorSpaceRef wxMacGetGenericRGBColorSpace() +{ + static wxCFRef genericRGBColorSpace; + + if (genericRGBColorSpace == NULL) + { + genericRGBColorSpace.reset( CGColorSpaceCreateWithName( kCGColorSpaceGenericRGB ) ); + } + + return genericRGBColorSpace; +} + +CGColorRef wxMacCreateCGColorFromHITheme( ThemeBrush brush ) +{ + CGColorRef color ; + HIThemeBrushCreateCGColor( brush, &color ); + return color; +} + +#if wxMAC_USE_QUICKDRAW + +static inline void PointFromHIPoint(const HIPoint& p, Point *pt) +{ + pt->h = wx_static_cast(short, p.x); + pt->v = wx_static_cast(short, p.y); +} + +void wxMacGlobalToLocal( WindowRef window , Point*pt ) +{ + HIPoint p = CGPointMake( pt->h, pt->v ); + HIViewRef contentView ; + // TODO check toolbar offset + HIViewFindByID( HIViewGetRoot( window ), kHIViewWindowContentID , &contentView) ; + HIPointConvert( &p, kHICoordSpace72DPIGlobal, NULL, kHICoordSpaceView, contentView ); + PointFromHIPoint(p, pt); +} + +void wxMacLocalToGlobal( WindowRef window , Point*pt ) +{ + HIPoint p = CGPointMake( pt->h, pt->v ); + HIViewRef contentView ; + // TODO check toolbar offset + HIViewFindByID( HIViewGetRoot( window ), kHIViewWindowContentID , &contentView) ; + HIPointConvert( &p, kHICoordSpaceView, contentView, kHICoordSpace72DPIGlobal, NULL ); + PointFromHIPoint(p, pt); +} + +#endif // wxMAC_USE_QUICKDRAW + +#endif // wxUSE_GUI + +#if wxUSE_BASE + +#endif diff --git a/src/osx/carbon/utilscocoa.mm b/src/osx/carbon/utilscocoa.mm new file mode 100644 index 0000000000..e2ac95f9e6 --- /dev/null +++ b/src/osx/carbon/utilscocoa.mm @@ -0,0 +1,304 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/utils.mm +// Purpose: various cocoa mixin utility functions +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id: utilscocoa.mm 48805 2007-09-19 14:52:25Z SC $ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include + +#ifdef __WXMAC__ +#include "wx/mac/private.h" +#endif + +#ifdef __WXMAC__ + +bool wxMacInitCocoa() +{ + bool cocoaLoaded = NSApplicationLoad(); + wxASSERT_MSG(cocoaLoaded,wxT("Couldn't load Cocoa in Carbon Environment")) ; + return cocoaLoaded; +} + +wxMacAutoreleasePool::wxMacAutoreleasePool() +{ + m_pool = [[NSAutoreleasePool alloc] init]; +} + +wxMacAutoreleasePool::~wxMacAutoreleasePool() +{ + [(NSAutoreleasePool*)m_pool release]; +} + +#endif + +#ifdef __WXCOCOCA__ + +CGContextRef wxMacGetContextFromCurrentNSContext() +{ + CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] + graphicsPort]; + return context; +} + +#endif + +// ---------------------------------------------------------------------------- +// NSObject Utils +// ---------------------------------------------------------------------------- + +void wxMacCocoaRelease( void* obj ) +{ + [(NSObject*)obj release]; +} + +void wxMacCocoaAutorelease( void* obj ) +{ + [(NSObject*)obj autorelease]; +} + +void wxMacCocoaRetain( void* obj ) +{ + [(NSObject*)obj retain]; +} + +// ---------------------------------------------------------------------------- +// NSImage Utils +// ---------------------------------------------------------------------------- + +// From "Cocoa Drawing Guide:Working with Images" +WX_NSImage CreateNSImageFromCGImage( CGImageRef image ) +{ + NSRect imageRect = NSMakeRect(0.0, 0.0, 0.0, 0.0); + + // Get the image dimensions. + imageRect.size.height = CGImageGetHeight(image); + imageRect.size.width = CGImageGetWidth(image); + + // Create a new image to receive the Quartz image data. + NSImage *newImage = [[NSImage alloc] initWithSize:imageRect.size]; + [newImage lockFocus]; + + // Get the Quartz context and draw. + CGContextRef imageContext = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]; + CGContextDrawImage( imageContext, *(CGRect*)&imageRect, image ); + [newImage unlockFocus]; + + return( newImage ); +} + +// ---------------------------------------------------------------------------- +// NSCursor Utils +// ---------------------------------------------------------------------------- + +#if wxMAC_USE_COCOA + +// copied from cursor.mm + +static NSCursor* wxGetStockCursor( short sIndex ) +{ + ClassicCursor* pCursor = &gMacCursors[sIndex]; + + //Classic mac cursors are 1bps 16x16 black and white with a + //identical mask that is 1 for on and 0 for off + NSImage *theImage = [[NSImage alloc] initWithSize:NSMakeSize(16.0,16.0)]; + + //NSCursor takes an NSImage takes a number of Representations - here + //we need only one for the raw data + NSBitmapImageRep *theRep = + [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:nil // Allocate the buffer for us :) + pixelsWide:16 + pixelsHigh:16 + bitsPerSample:1 + samplesPerPixel:2 + hasAlpha:YES // Well, more like a mask... + isPlanar:NO + colorSpaceName:NSCalibratedWhiteColorSpace // Normal B/W - 0 black 1 white + bytesPerRow:0 // I don't care - figure it out for me :) + bitsPerPixel:2]; // bitsPerSample * samplesPerPixel + + //unsigned int is better to put data in then a void* + //note that working with bitfields would be a lot better here - + //but since it breaks some compilers... + wxUint32 *data = (wxUint32 *)[theRep bitmapData]; + + //traverse through the bitmap data + for (int i = 0; i < 16; ++i) + { + //bit alpha bit alpha ... :D + + //Notice the = instead of |= - + //this is to avoid doing a memset earlier + data[i] = 0; + + //do the rest of those bits and alphas :) + for (int shift = 0; shift < 32; ++shift) + { + const int bit = 1 << (shift >> 1); + data[i] |= ( !!( (pCursor->mask[i] & bit) ) ) << shift; + data[i] |= ( !( (pCursor->bits[i] & bit) ) ) << ++shift; + } + } + + //add the representation (data) to the image + [theImage addRepresentation:theRep]; + + //create the new cursor + NSCursor* theCursor = [[NSCursor alloc] initWithImage:theImage + hotSpot:NSMakePoint(pCursor->hotspot[1], pCursor->hotspot[0]) + ]; + + //do the usual cleanups + [theRep release]; + [theImage release]; + + //return the new cursor + return theCursor; +} + +WX_NSCursor wxMacCocoaCreateStockCursor( int cursor_type ) +{ + WX_NSCursor cursor = nil; + switch (cursor_type) + { + case wxCURSOR_COPY_ARROW: + cursor = [[NSCursor arrowCursor] retain]; + break; + + case wxCURSOR_WATCH: + case wxCURSOR_WAIT: + // should be displayed by the system when things are running + cursor = [[NSCursor arrowCursor] retain]; + break; + + case wxCURSOR_IBEAM: + cursor = [[NSCursor IBeamCursor] retain]; + break; + + case wxCURSOR_CROSS: + cursor = [[NSCursor crosshairCursor] retain]; + break; + + case wxCURSOR_SIZENWSE: + cursor = wxGetStockCursor(kwxCursorSizeNWSE); + break; + + case wxCURSOR_SIZENESW: + cursor = wxGetStockCursor(kwxCursorSizeNESW); + break; + + case wxCURSOR_SIZEWE: + cursor = [[NSCursor resizeLeftRightCursor] retain]; + break; + + case wxCURSOR_SIZENS: + cursor = [[NSCursor resizeUpDownCursor] retain]; + break; + + case wxCURSOR_SIZING: + cursor = wxGetStockCursor(kwxCursorSize); + break; + + case wxCURSOR_HAND: + cursor = [[NSCursor pointingHandCursor] retain]; + break; + + case wxCURSOR_BULLSEYE: + cursor = wxGetStockCursor(kwxCursorBullseye); + break; + + case wxCURSOR_PENCIL: + cursor = wxGetStockCursor(kwxCursorPencil); + break; + + case wxCURSOR_MAGNIFIER: + cursor = wxGetStockCursor(kwxCursorMagnifier); + break; + + case wxCURSOR_NO_ENTRY: + cursor = wxGetStockCursor(kwxCursorNoEntry); + break; + + case wxCURSOR_PAINT_BRUSH: + cursor = wxGetStockCursor(kwxCursorPaintBrush); + break; + + case wxCURSOR_POINT_LEFT: + cursor = wxGetStockCursor(kwxCursorPointLeft); + break; + + case wxCURSOR_POINT_RIGHT: + cursor = wxGetStockCursor(kwxCursorPointRight); + break; + + case wxCURSOR_QUESTION_ARROW: + cursor = wxGetStockCursor(kwxCursorQuestionArrow); + break; + + case wxCURSOR_BLANK: + cursor = wxGetStockCursor(kwxCursorBlank); + break; + + case wxCURSOR_RIGHT_ARROW: + cursor = wxGetStockCursor(kwxCursorRightArrow); + break; + + case wxCURSOR_SPRAYCAN: + cursor = wxGetStockCursor(kwxCursorRoller); + break; + + case wxCURSOR_CHAR: + case wxCURSOR_ARROW: + case wxCURSOR_LEFT_BUTTON: + case wxCURSOR_RIGHT_BUTTON: + case wxCURSOR_MIDDLE_BUTTON: + default: + cursor = [[NSCursor arrowCursor] retain]; + break; + } + return cursor; +} + +// C-based style wrapper routines around NSCursor +WX_NSCursor wxMacCocoaCreateCursorFromCGImage( CGImageRef cgImageRef, float hotSpotX, float hotSpotY ) +{ + static BOOL firstTime = YES; + + if ( firstTime ) + { + // Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image + [[[NSWindow alloc] init] release]; + firstTime = NO; + } + + NSImage *nsImage = CreateNSImageFromCGImage( cgImageRef ); + NSCursor *cursor = [[NSCursor alloc] initWithImage:nsImage hotSpot:NSMakePoint( hotSpotX, hotSpotY )]; + + [nsImage release]; + + return cursor; +} + +void wxMacCocoaSetCursor( WX_NSCursor cursor ) +{ + [cursor set]; +} + +void wxMacCocoaHideCursor() +{ + [NSCursor hide]; +} + +void wxMacCocoaShowCursor() +{ + [NSCursor unhide]; +} + +#endif diff --git a/src/osx/carbon/window.cpp b/src/osx/carbon/window.cpp new file mode 100644 index 0000000000..9a37721407 --- /dev/null +++ b/src/osx/carbon/window.cpp @@ -0,0 +1,3277 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/carbon/window.cpp +// Purpose: wxWindowMac +// Author: Stefan Csomor +// Modified by: +// Created: 1998-01-01 +// RCS-ID: $Id$ +// Copyright: (c) Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" + +#include "wx/window.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/panel.h" + #include "wx/frame.h" + #include "wx/dc.h" + #include "wx/dcclient.h" + #include "wx/button.h" + #include "wx/menu.h" + #include "wx/dialog.h" + #include "wx/settings.h" + #include "wx/msgdlg.h" + #include "wx/scrolbar.h" + #include "wx/statbox.h" + #include "wx/textctrl.h" + #include "wx/toolbar.h" + #include "wx/layout.h" + #include "wx/statusbr.h" + #include "wx/menuitem.h" + #include "wx/treectrl.h" + #include "wx/listctrl.h" +#endif + +#include "wx/tooltip.h" +#include "wx/spinctrl.h" +#include "wx/geometry.h" + +#if wxUSE_LISTCTRL + #include "wx/listctrl.h" +#endif + +#if wxUSE_TREECTRL + #include "wx/treectrl.h" +#endif + +#if wxUSE_CARET + #include "wx/caret.h" +#endif + +#if wxUSE_POPUPWIN + #include "wx/popupwin.h" +#endif + +#if wxUSE_DRAG_AND_DROP +#include "wx/dnd.h" +#endif + +#include "wx/mac/uma.h" + +#define MAC_SCROLLBAR_SIZE 15 +#define MAC_SMALL_SCROLLBAR_SIZE 11 + +#include + +#ifdef __WXUNIVERSAL__ + IMPLEMENT_ABSTRACT_CLASS(wxWindowMac, wxWindowBase) +#else + IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase) +#endif + +BEGIN_EVENT_TABLE(wxWindowMac, wxWindowBase) + EVT_NC_PAINT(wxWindowMac::OnNcPaint) + EVT_ERASE_BACKGROUND(wxWindowMac::OnEraseBackground) + EVT_PAINT(wxWindowMac::OnPaint) + EVT_MOUSE_EVENTS(wxWindowMac::OnMouseEvent) +END_EVENT_TABLE() + +#define wxMAC_DEBUG_REDRAW 0 +#ifndef wxMAC_DEBUG_REDRAW +#define wxMAC_DEBUG_REDRAW 0 +#endif + +// --------------------------------------------------------------------------- +// Utility Routines to move between different coordinate systems +// --------------------------------------------------------------------------- + +/* + * Right now we have the following setup : + * a border that is not part of the native control is always outside the + * control's border (otherwise we loose all native intelligence, future ways + * may be to have a second embedding control responsible for drawing borders + * and backgrounds eventually) + * so all this border calculations have to be taken into account when calling + * native methods or getting native oriented data + * so we have three coordinate systems here + * wx client coordinates + * wx window coordinates (including window frames) + * native coordinates + */ + +// +// originating from native control +// + + +void wxMacNativeToWindow( const wxWindow* window , RgnHandle handle ) +{ + OffsetRgn( handle , window->MacGetLeftBorderSize() , window->MacGetTopBorderSize() ) ; +} + +void wxMacNativeToWindow( const wxWindow* window , Rect *rect ) +{ + OffsetRect( rect , window->MacGetLeftBorderSize() , window->MacGetTopBorderSize() ) ; +} + +// +// directed towards native control +// + +void wxMacWindowToNative( const wxWindow* window , RgnHandle handle ) +{ + OffsetRgn( handle , -window->MacGetLeftBorderSize() , -window->MacGetTopBorderSize() ); +} + +void wxMacWindowToNative( const wxWindow* window , Rect *rect ) +{ + OffsetRect( rect , -window->MacGetLeftBorderSize() , -window->MacGetTopBorderSize() ) ; +} + +// --------------------------------------------------------------------------- +// Carbon Events +// --------------------------------------------------------------------------- + +static const EventTypeSpec eventList[] = +{ + { kEventClassCommand, kEventProcessCommand } , + { kEventClassCommand, kEventCommandUpdateStatus } , + + { kEventClassControl , kEventControlGetClickActivation } , + { kEventClassControl , kEventControlHit } , + + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } , + { kEventClassTextInput, kEventTextInputUpdateActiveInputArea } , + + { kEventClassControl , kEventControlDraw } , + + { kEventClassControl , kEventControlVisibilityChanged } , + { kEventClassControl , kEventControlEnabledStateChanged } , + { kEventClassControl , kEventControlHiliteChanged } , + + { kEventClassControl , kEventControlActivate } , + { kEventClassControl , kEventControlDeactivate } , + + { kEventClassControl , kEventControlSetFocusPart } , + { kEventClassControl , kEventControlFocusPartChanged } , + + { kEventClassService , kEventServiceGetTypes }, + { kEventClassService , kEventServiceCopy }, + { kEventClassService , kEventServicePaste }, + +// { kEventClassControl , kEventControlInvalidateForSizeChange } , // 10.3 only +// { kEventClassControl , kEventControlBoundsChanged } , +} ; + +static pascal OSStatus wxMacWindowControlEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + static wxWindowMac* targetFocusWindow = NULL; + static wxWindowMac* formerFocusWindow = NULL; + + wxMacCarbonEvent cEvent( event ) ; + + ControlRef controlRef ; + wxWindowMac* thisWindow = (wxWindowMac*) data ; + + cEvent.GetParameter( kEventParamDirectObject , &controlRef ) ; + + switch ( GetEventKind( event ) ) + { + case kEventControlDraw : + { + RgnHandle updateRgn = NULL ; + RgnHandle allocatedRgn = NULL ; + wxRegion visRegion = thisWindow->MacGetVisibleRegion() ; + + if ( cEvent.GetParameter(kEventParamRgnHandle, &updateRgn) != noErr ) + { + HIShapeGetAsQDRgn( visRegion.GetWXHRGN(), updateRgn ); + } + else + { + if ( thisWindow->MacGetLeftBorderSize() != 0 || thisWindow->MacGetTopBorderSize() != 0 ) + { + // as this update region is in native window locals we must adapt it to wx window local + allocatedRgn = NewRgn() ; + CopyRgn( updateRgn , allocatedRgn ) ; + + // hide the given region by the new region that must be shifted + wxMacNativeToWindow( thisWindow , allocatedRgn ) ; + updateRgn = allocatedRgn ; + } + } + +#if wxMAC_DEBUG_REDRAW + if ( thisWindow->MacIsUserPane() ) + { + static float color = 0.5 ; + static int channel = 0 ; + HIRect bounds; + CGContextRef cgContext = cEvent.GetParameter(kEventParamCGContextRef) ; + + HIViewGetBounds( controlRef, &bounds ); + CGContextSetRGBFillColor( cgContext, channel == 0 ? color : 0.5 , + channel == 1 ? color : 0.5 , channel == 2 ? color : 0.5 , 1 ); + CGContextFillRect( cgContext, bounds ); + color += 0.1 ; + if ( color > 0.9 ) + { + color = 0.5 ; + channel++ ; + if ( channel == 3 ) + channel = 0 ; + } + } +#endif + + { + bool created = false ; + CGContextRef cgContext = NULL ; + OSStatus err = cEvent.GetParameter(kEventParamCGContextRef, &cgContext) ; + if ( err != noErr ) + { + wxFAIL_MSG("Unable to retrieve CGContextRef"); + } + + thisWindow->MacSetCGContextRef( cgContext ) ; + + { + wxMacCGContextStateSaver sg( cgContext ) ; + CGFloat alpha = (CGFloat)1.0 ; + { + wxWindow* iter = thisWindow ; + while ( iter ) + { + alpha *= (CGFloat)( iter->GetTransparent()/255.0 ) ; + if ( iter->IsTopLevel() ) + iter = NULL ; + else + iter = iter->GetParent() ; + } + } + CGContextSetAlpha( cgContext , alpha ) ; + + if ( thisWindow->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT ) + { + HIRect bounds; + HIViewGetBounds( controlRef, &bounds ); + CGContextClearRect( cgContext, bounds ); + } + + + + if ( thisWindow->MacDoRedraw( updateRgn , cEvent.GetTicks() ) ) + result = noErr ; + + thisWindow->MacSetCGContextRef( NULL ) ; + } + + if ( created ) + CGContextRelease( cgContext ) ; + } + + if ( allocatedRgn ) + DisposeRgn( allocatedRgn ) ; + } + break ; + + case kEventControlVisibilityChanged : + // we might have two native controls attributed to the same wxWindow instance + // eg a scrollview and an embedded textview, make sure we only fire for the 'outer' + // control, as otherwise native and wx visibility are different + if ( thisWindow->GetPeer() != NULL && thisWindow->GetPeer()->GetControlRef() == controlRef ) + { + thisWindow->MacVisibilityChanged() ; + } + break ; + + case kEventControlEnabledStateChanged : + thisWindow->MacEnabledStateChanged(); + break ; + + case kEventControlHiliteChanged : + thisWindow->MacHiliteChanged() ; + break ; + + case kEventControlActivate : + case kEventControlDeactivate : + // FIXME: we should have a virtual function for this! +#if wxUSE_TREECTRL + if ( thisWindow->IsKindOf( CLASSINFO( wxTreeCtrl ) ) ) + thisWindow->Refresh(); +#endif +#if wxUSE_LISTCTRL + if ( thisWindow->IsKindOf( CLASSINFO( wxListCtrl ) ) ) + thisWindow->Refresh(); +#endif + break ; + + // + // focus handling + // different handling on OS X + // + + case kEventControlFocusPartChanged : + // the event is emulated by wxmac for systems lower than 10.5 + { + if ( UMAGetSystemVersion() < 0x1050 ) + { + // as it is synthesized here, we have to manually avoid propagation + result = noErr; + } + ControlPartCode previousControlPart = cEvent.GetParameter(kEventParamControlPreviousPart , typeControlPartCode ); + ControlPartCode currentControlPart = cEvent.GetParameter(kEventParamControlCurrentPart , typeControlPartCode ); + + if ( thisWindow->MacGetTopLevelWindow() && thisWindow->GetPeer()->NeedsFocusRect() ) + { + thisWindow->MacInvalidateBorders(); + } + + if ( currentControlPart == 0 ) + { + // kill focus +#if wxUSE_CARET + if ( thisWindow->GetCaret() ) + thisWindow->GetCaret()->OnKillFocus(); +#endif + + wxLogTrace(_T("Focus"), _T("focus lost(%p)"), wx_static_cast(void*, thisWindow)); + + // remove this as soon as posting the synthesized event works properly + static bool inKillFocusEvent = false ; + + if ( !inKillFocusEvent ) + { + inKillFocusEvent = true ; + wxFocusEvent event( wxEVT_KILL_FOCUS, thisWindow->GetId()); + event.SetEventObject(thisWindow); + event.SetWindow(targetFocusWindow); + thisWindow->HandleWindowEvent(event) ; + inKillFocusEvent = false ; + targetFocusWindow = NULL; + } + } + else if ( previousControlPart == 0 ) + { + // set focus + // panel wants to track the window which was the last to have focus in it + wxLogTrace(_T("Focus"), _T("focus set(%p)"), wx_static_cast(void*, thisWindow)); + wxChildFocusEvent eventFocus((wxWindow*)thisWindow); + thisWindow->HandleWindowEvent(eventFocus); + +#if wxUSE_CARET + if ( thisWindow->GetCaret() ) + thisWindow->GetCaret()->OnSetFocus(); +#endif + + wxFocusEvent event(wxEVT_SET_FOCUS, thisWindow->GetId()); + event.SetEventObject(thisWindow); + event.SetWindow(formerFocusWindow); + thisWindow->HandleWindowEvent(event) ; + formerFocusWindow = NULL; + } + } + break; + case kEventControlSetFocusPart : + { + Boolean focusEverything = false ; + if ( cEvent.GetParameter(kEventParamControlFocusEverything , &focusEverything ) == noErr ) + { + // put a breakpoint here to catch focus everything events + } + ControlPartCode controlPart = cEvent.GetParameter(kEventParamControlPart , typeControlPartCode ); + if ( controlPart != kControlFocusNoPart ) + { + targetFocusWindow = thisWindow; + wxLogTrace(_T("Focus"), _T("focus to be set(%p)"), wx_static_cast(void*, thisWindow)); + } + else + { + formerFocusWindow = thisWindow; + wxLogTrace(_T("Focus"), _T("focus to be lost(%p)"), wx_static_cast(void*, thisWindow)); + } + + ControlPartCode previousControlPart = 0; + verify_noerr( HIViewGetFocusPart(controlRef, &previousControlPart)); + + if ( thisWindow->MacIsUserPane() ) + { + if ( controlPart != kControlFocusNoPart ) + cEvent.SetParameter( kEventParamControlPart, typeControlPartCode, 1 ) ; + result = noErr ; + } + else + result = CallNextEventHandler(handler, event); + + if ( UMAGetSystemVersion() < 0x1050 ) + { +// set back to 0 if problems arise +#if 1 + if ( result == noErr ) + { + ControlPartCode currentControlPart = cEvent.GetParameter(kEventParamControlPart , typeControlPartCode ); + // synthesize the event focus changed event + EventRef evRef = NULL ; + + OSStatus err = MacCreateEvent( + NULL , kEventClassControl , kEventControlFocusPartChanged , TicksToEventTime( TickCount() ) , + kEventAttributeUserEvent , &evRef ); + verify_noerr( err ); + + wxMacCarbonEvent iEvent( evRef ) ; + iEvent.SetParameter( kEventParamDirectObject , controlRef ); + iEvent.SetParameter( kEventParamPostTarget, typeEventTargetRef, GetControlEventTarget( controlRef ) ); + iEvent.SetParameter( kEventParamControlPreviousPart, typeControlPartCode, previousControlPart ); + iEvent.SetParameter( kEventParamControlCurrentPart, typeControlPartCode, currentControlPart ); + +#if 1 + // TODO test this first, avoid double posts etc... + PostEventToQueue( GetMainEventQueue(), evRef , kEventPriorityHigh ); +#else + wxMacWindowControlEventHandler( NULL , evRef , data ) ; +#endif + ReleaseEvent( evRef ) ; + } +#else + // old implementation, to be removed if the new one works + if ( controlPart == kControlFocusNoPart ) + { +#if wxUSE_CARET + if ( thisWindow->GetCaret() ) + thisWindow->GetCaret()->OnKillFocus(); +#endif + + wxLogTrace(_T("Focus"), _T("focus lost(%p)"), wx_static_cast(void*, thisWindow)); + + static bool inKillFocusEvent = false ; + + if ( !inKillFocusEvent ) + { + inKillFocusEvent = true ; + wxFocusEvent event( wxEVT_KILL_FOCUS, thisWindow->GetId()); + event.SetEventObject(thisWindow); + thisWindow->HandleWindowEvent(event) ; + inKillFocusEvent = false ; + } + } + else + { + // panel wants to track the window which was the last to have focus in it + wxLogTrace(_T("Focus"), _T("focus set(%p)"), wx_static_cast(void*, thisWindow)); + wxChildFocusEvent eventFocus((wxWindow*)thisWindow); + thisWindow->HandleWindowEvent(eventFocus); + + #if wxUSE_CARET + if ( thisWindow->GetCaret() ) + thisWindow->GetCaret()->OnSetFocus(); + #endif + + wxFocusEvent event(wxEVT_SET_FOCUS, thisWindow->GetId()); + event.SetEventObject(thisWindow); + thisWindow->HandleWindowEvent(event) ; + } +#endif + } + } + break ; + + case kEventControlHit : + result = thisWindow->MacControlHit( handler , event ) ; + break ; + + case kEventControlGetClickActivation : + { + // fix to always have a proper activation for DataBrowser controls (stay in bkgnd otherwise) + WindowRef owner = cEvent.GetParameter(kEventParamWindowRef); + if ( !IsWindowActive(owner) ) + { + cEvent.SetParameter(kEventParamClickActivation,(UInt32) kActivateAndIgnoreClick) ; + result = noErr ; + } + } + break ; + + default : + break ; + } + + return result ; +} + +static pascal OSStatus +wxMacWindowServiceEventHandler(EventHandlerCallRef WXUNUSED(handler), + EventRef event, + void *data) +{ + OSStatus result = eventNotHandledErr ; + + wxMacCarbonEvent cEvent( event ) ; + + ControlRef controlRef ; + wxWindowMac* thisWindow = (wxWindowMac*) data ; + wxTextCtrl* textCtrl = wxDynamicCast( thisWindow , wxTextCtrl ) ; + cEvent.GetParameter( kEventParamDirectObject , &controlRef ) ; + + switch ( GetEventKind( event ) ) + { + case kEventServiceGetTypes : + if ( textCtrl ) + { + long from, to ; + textCtrl->GetSelection( &from , &to ) ; + + CFMutableArrayRef copyTypes = 0 , pasteTypes = 0; + if ( from != to ) + copyTypes = cEvent.GetParameter< CFMutableArrayRef >( kEventParamServiceCopyTypes , typeCFMutableArrayRef ) ; + if ( textCtrl->IsEditable() ) + pasteTypes = cEvent.GetParameter< CFMutableArrayRef >( kEventParamServicePasteTypes , typeCFMutableArrayRef ) ; + + static const OSType textDataTypes[] = { kTXNTextData /* , 'utxt', 'PICT', 'MooV', 'AIFF' */ }; + for ( size_t i = 0 ; i < WXSIZEOF(textDataTypes) ; ++i ) + { + CFStringRef typestring = CreateTypeStringWithOSType(textDataTypes[i]); + if ( typestring ) + { + if ( copyTypes ) + CFArrayAppendValue(copyTypes, typestring) ; + if ( pasteTypes ) + CFArrayAppendValue(pasteTypes, typestring) ; + + CFRelease( typestring ) ; + } + } + + result = noErr ; + } + break ; + + case kEventServiceCopy : + if ( textCtrl ) + { + long from, to ; + + textCtrl->GetSelection( &from , &to ) ; + wxString val = textCtrl->GetValue() ; + val = val.Mid( from , to - from ) ; + PasteboardRef pasteboard = cEvent.GetParameter( kEventParamPasteboardRef, typePasteboardRef ); + verify_noerr( PasteboardClear( pasteboard ) ) ; + PasteboardSynchronize( pasteboard ); + // TODO add proper conversion + CFDataRef data = CFDataCreate( kCFAllocatorDefault, (const UInt8*)val.c_str(), val.length() ); + PasteboardPutItemFlavor( pasteboard, (PasteboardItemID) 1, CFSTR("com.apple.traditional-mac-plain-text"), data, 0); + CFRelease( data ); + result = noErr ; + } + break ; + + case kEventServicePaste : + if ( textCtrl ) + { + PasteboardRef pasteboard = cEvent.GetParameter( kEventParamPasteboardRef, typePasteboardRef ); + PasteboardSynchronize( pasteboard ); + ItemCount itemCount; + verify_noerr( PasteboardGetItemCount( pasteboard, &itemCount ) ); + for( UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex++ ) + { + PasteboardItemID itemID; + if ( PasteboardGetItemIdentifier( pasteboard, itemIndex, &itemID ) == noErr ) + { + CFDataRef flavorData = NULL; + if ( PasteboardCopyItemFlavorData( pasteboard, itemID, CFSTR("com.apple.traditional-mac-plain-text"), &flavorData ) == noErr ) + { + CFIndex flavorDataSize = CFDataGetLength( flavorData ); + char *content = new char[flavorDataSize+1] ; + memcpy( content, CFDataGetBytePtr( flavorData ), flavorDataSize ); + content[flavorDataSize]=0; + CFRelease( flavorData ); +#if wxUSE_UNICODE + textCtrl->WriteText( wxString( content , wxConvLocal ) ); +#else + textCtrl->WriteText( wxString( content ) ) ; +#endif + + delete[] content ; + result = noErr ; + } + } + } + } + break ; + + default: + break ; + } + + return result ; +} + +pascal OSStatus wxMacUnicodeTextEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + OSStatus result = eventNotHandledErr ; + wxWindowMac* focus = (wxWindowMac*) data ; + + wchar_t* uniChars = NULL ; + UInt32 when = EventTimeToTicks( GetEventTime( event ) ) ; + + UniChar* charBuf = NULL; + ByteCount dataSize = 0 ; + int numChars = 0 ; + UniChar buf[2] ; + if ( GetEventParameter( event, kEventParamTextInputSendText, typeUnicodeText, NULL, 0 , &dataSize, NULL ) == noErr ) + { + numChars = dataSize / sizeof( UniChar) + 1; + charBuf = buf ; + + if ( (size_t) numChars * 2 > sizeof(buf) ) + charBuf = new UniChar[ numChars ] ; + else + charBuf = buf ; + + uniChars = new wchar_t[ numChars ] ; + GetEventParameter( event, kEventParamTextInputSendText, typeUnicodeText, NULL, dataSize , NULL , charBuf ) ; + charBuf[ numChars - 1 ] = 0; +#if SIZEOF_WCHAR_T == 2 + uniChars = (wchar_t*) charBuf ; +/* memcpy( uniChars , charBuf , numChars * 2 ) ;*/ // is there any point in copying charBuf over itself? (in fact, memcpy isn't even guaranteed to work correctly if the source and destination ranges overlap...) +#else + // the resulting string will never have more chars than the utf16 version, so this is safe + wxMBConvUTF16 converter ; + numChars = converter.MB2WC( uniChars , (const char*)charBuf , numChars ) ; +#endif + } + + switch ( GetEventKind( event ) ) + { + case kEventTextInputUpdateActiveInputArea : + { + // An IME input event may return several characters, but we need to send one char at a time to + // EVT_CHAR + for (int pos=0 ; pos < numChars ; pos++) + { + WXEVENTREF formerEvent = wxTheApp->MacGetCurrentEvent() ; + WXEVENTHANDLERCALLREF formerHandler = wxTheApp->MacGetCurrentEventHandlerCallRef() ; + wxTheApp->MacSetCurrentEvent( event , handler ) ; + + UInt32 message = uniChars[pos] < 128 ? (char)uniChars[pos] : '?'; +/* + NB: faking a charcode here is problematic. The kEventTextInputUpdateActiveInputArea event is sent + multiple times to update the active range during inline input, so this handler will often receive + uncommited text, which should usually not trigger side effects. It might be a good idea to check the + kEventParamTextInputSendFixLen parameter and verify if input is being confirmed (see CarbonEvents.h). + On the other hand, it can be useful for some applications to react to uncommitted text (for example, + to update a status display), as long as it does not disrupt the inline input session. Ideally, wx + should add new event types to support advanced text input. For now, I would keep things as they are. + + However, the code that was being used caused additional problems: + UInt32 message = (0 << 8) + ((char)uniChars[pos] ); + Since it simply truncated the unichar to the last byte, it ended up causing weird bugs with inline + input, such as switching to another field when one attempted to insert the character U+4E09 (the kanji + for "three"), because it was truncated to 09 (kTabCharCode), which was later "converted" to WXK_TAB + (still 09) in wxMacTranslateKey; or triggering the default button when one attempted to insert U+840D + (the kanji for "name"), which got truncated to 0D and interpreted as a carriage return keypress. + Note that even single-byte characters could have been misinterpreted, since MacRoman charcodes only + overlap with Unicode within the (7-bit) ASCII range. + But simply passing a NUL charcode would disable text updated events, because wxTextCtrl::OnChar checks + for codes within a specific range. Therefore I went for the solution seen above, which keeps ASCII + characters as they are and replaces the rest with '?', ensuring that update events are triggered. + It would be better to change wxTextCtrl::OnChar to look at the actual unicode character instead, but + I don't have time to look into that right now. + -- CL +*/ + if ( wxTheApp->MacSendCharEvent( + focus , message , 0 , when , 0 , 0 , uniChars[pos] ) ) + { + result = noErr ; + } + + wxTheApp->MacSetCurrentEvent( formerEvent , formerHandler ) ; + } + } + break ; + case kEventTextInputUnicodeForKeyEvent : + { + UInt32 keyCode, modifiers ; + Point point ; + EventRef rawEvent ; + unsigned char charCode ; + + GetEventParameter( event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawEvent), NULL, &rawEvent ) ; + GetEventParameter( rawEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &charCode ); + GetEventParameter( rawEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode ); + GetEventParameter( rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers ); + GetEventParameter( rawEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &point ); + + UInt32 message = (keyCode << 8) + charCode; + + // An IME input event may return several characters, but we need to send one char at a time to + // EVT_CHAR + for (int pos=0 ; pos < numChars ; pos++) + { + WXEVENTREF formerEvent = wxTheApp->MacGetCurrentEvent() ; + WXEVENTHANDLERCALLREF formerHandler = wxTheApp->MacGetCurrentEventHandlerCallRef() ; + wxTheApp->MacSetCurrentEvent( event , handler ) ; + + if ( wxTheApp->MacSendCharEvent( + focus , message , modifiers , when , point.h , point.v , uniChars[pos] ) ) + { + result = noErr ; + } + + wxTheApp->MacSetCurrentEvent( formerEvent , formerHandler ) ; + } + } + break; + default: + break ; + } + + delete [] uniChars ; + if ( charBuf != buf ) + delete [] charBuf ; + + return result ; +} + +static pascal OSStatus +wxMacWindowCommandEventHandler(EventHandlerCallRef WXUNUSED(handler), + EventRef event, + void *data) +{ + OSStatus result = eventNotHandledErr ; + wxWindowMac* focus = (wxWindowMac*) data ; + + HICommand command ; + + wxMacCarbonEvent cEvent( event ) ; + cEvent.GetParameter(kEventParamDirectObject,typeHICommand,&command) ; + + wxMenuItem* item = NULL ; + wxMenu* itemMenu = wxFindMenuFromMacCommand( command , item ) ; + int id = wxMacCommandToId( command.commandID ) ; + + if ( item ) + { + wxASSERT( itemMenu != NULL ) ; + + switch ( cEvent.GetKind() ) + { + case kEventProcessCommand : + result = itemMenu->MacHandleCommandProcess( item, id, focus ); + break ; + + case kEventCommandUpdateStatus: + result = itemMenu->MacHandleCommandUpdateStatus( item, id, focus ); + break ; + + default : + break ; + } + } + return result ; +} + +pascal OSStatus wxMacWindowEventHandler( EventHandlerCallRef handler , EventRef event , void *data ) +{ + EventRef formerEvent = (EventRef) wxTheApp->MacGetCurrentEvent() ; + EventHandlerCallRef formerEventHandlerCallRef = (EventHandlerCallRef) wxTheApp->MacGetCurrentEventHandlerCallRef() ; + wxTheApp->MacSetCurrentEvent( event , handler ) ; + OSStatus result = eventNotHandledErr ; + + switch ( GetEventClass( event ) ) + { + case kEventClassCommand : + result = wxMacWindowCommandEventHandler( handler , event , data ) ; + break ; + + case kEventClassControl : + result = wxMacWindowControlEventHandler( handler, event, data ) ; + break ; + + case kEventClassService : + result = wxMacWindowServiceEventHandler( handler, event , data ) ; + break ; + + case kEventClassTextInput : + result = wxMacUnicodeTextEventHandler( handler , event , data ) ; + break ; + + default : + break ; + } + + wxTheApp->MacSetCurrentEvent( formerEvent, formerEventHandlerCallRef ) ; + + return result ; +} + +DEFINE_ONE_SHOT_HANDLER_GETTER( wxMacWindowEventHandler ) + +// --------------------------------------------------------------------------- +// Scrollbar Tracking for all +// --------------------------------------------------------------------------- + +pascal void wxMacLiveScrollbarActionProc( ControlRef control , ControlPartCode partCode ) ; +pascal void wxMacLiveScrollbarActionProc( ControlRef control , ControlPartCode partCode ) +{ + if ( partCode != 0) + { + wxWindow* wx = wxFindControlFromMacControl( control ) ; + if ( wx ) + wx->MacHandleControlClick( (WXWidget) control , partCode , true /* stillDown */ ) ; + } +} +wxMAC_DEFINE_PROC_GETTER( ControlActionUPP , wxMacLiveScrollbarActionProc ) ; + +// =========================================================================== +// implementation +// =========================================================================== + +WX_DECLARE_HASH_MAP(ControlRef, wxWindow*, wxPointerHash, wxPointerEqual, MacControlMap); + +static MacControlMap wxWinMacControlList; + +wxWindow *wxFindControlFromMacControl(ControlRef inControl ) +{ + MacControlMap::iterator node = wxWinMacControlList.find(inControl); + + return (node == wxWinMacControlList.end()) ? NULL : node->second; +} + +void wxAssociateControlWithMacControl(ControlRef inControl, wxWindow *control) +{ + // adding NULL ControlRef is (first) surely a result of an error and + // (secondly) breaks native event processing + wxCHECK_RET( inControl != (ControlRef) NULL, wxT("attempt to add a NULL WindowRef to window list") ); + + wxWinMacControlList[inControl] = control; +} + +void wxRemoveMacControlAssociation(wxWindow *control) +{ + // iterate over all the elements in the class + // is the iterator stable ? as we might have two associations pointing to the same wxWindow + // we should go on... + + bool found = true ; + while ( found ) + { + found = false ; + MacControlMap::iterator it; + for ( it = wxWinMacControlList.begin(); it != wxWinMacControlList.end(); ++it ) + { + if ( it->second == control ) + { + wxWinMacControlList.erase(it); + found = true ; + break; + } + } + } +} + +// ---------------------------------------------------------------------------- + // constructors and such +// ---------------------------------------------------------------------------- + +wxWindowMac::wxWindowMac() +{ + Init(); +} + +wxWindowMac::wxWindowMac(wxWindowMac *parent, + wxWindowID id, + const wxPoint& pos , + const wxSize& size , + long style , + const wxString& name ) +{ + Init(); + Create(parent, id, pos, size, style, name); +} + +void wxWindowMac::Init() +{ + m_peer = NULL ; + m_macAlpha = 255 ; + m_cgContextRef = NULL ; + + // as all windows are created with WS_VISIBLE style... + m_isShown = true; + + m_hScrollBar = NULL ; + m_vScrollBar = NULL ; + m_hScrollBarAlwaysShown = false; + m_vScrollBarAlwaysShown = false; + + m_macIsUserPane = true; + m_clipChildren = false ; + m_cachedClippedRectValid = false ; +} + +wxWindowMac::~wxWindowMac() +{ + SendDestroyEvent(); + + m_isBeingDeleted = true; + + MacInvalidateBorders() ; + +#ifndef __WXUNIVERSAL__ + // VS: make sure there's no wxFrame with last focus set to us: + for ( wxWindow *win = GetParent(); win; win = win->GetParent() ) + { + wxFrame *frame = wxDynamicCast(win, wxFrame); + if ( frame ) + { + if ( frame->GetLastFocus() == this ) + frame->SetLastFocus((wxWindow*)NULL); + break; + } + } +#endif + + // destroy children before destroying this window itself + DestroyChildren(); + + // wxRemoveMacControlAssociation( this ) ; + // If we delete an item, we should initialize the parent panel, + // because it could now be invalid. + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); + if ( tlw ) + { + if ( tlw->GetDefaultItem() == (wxButton*) this) + tlw->SetDefaultItem(NULL); + } + + if ( m_peer && m_peer->Ok() ) + { + // in case the callback might be called during destruction + wxRemoveMacControlAssociation( this) ; + ::RemoveEventHandler( (EventHandlerRef ) m_macControlEventHandler ) ; + // we currently are not using this hook + // ::SetControlColorProc( *m_peer , NULL ) ; + m_peer->Dispose() ; + } + + if ( g_MacLastWindow == this ) + g_MacLastWindow = NULL ; + +#ifndef __WXUNIVERSAL__ + wxFrame* frame = wxDynamicCast( wxGetTopLevelParent( (wxWindow*)this ) , wxFrame ) ; + if ( frame ) + { + if ( frame->GetLastFocus() == this ) + frame->SetLastFocus( NULL ) ; + } +#endif + + // delete our drop target if we've got one +#if wxUSE_DRAG_AND_DROP + if ( m_dropTarget != NULL ) + { + delete m_dropTarget; + m_dropTarget = NULL; + } +#endif + + delete m_peer ; +} + +WXWidget wxWindowMac::GetHandle() const +{ + return (WXWidget) m_peer->GetControlRef() ; +} + +void wxWindowMac::MacInstallEventHandler( WXWidget control ) +{ + wxAssociateControlWithMacControl( (ControlRef) control , this ) ; + InstallControlEventHandler( (ControlRef)control , GetwxMacWindowEventHandlerUPP(), + GetEventTypeCount(eventList), eventList, this, + (EventHandlerRef *)&m_macControlEventHandler); +} + +// Constructor +bool wxWindowMac::Create(wxWindowMac *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) +{ + wxCHECK_MSG( parent, false, wxT("can't create wxWindowMac without parent") ); + + if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) + return false; + + m_windowVariant = parent->GetWindowVariant() ; + + if ( m_macIsUserPane ) + { + Rect bounds = wxMacGetBoundsForControl( this , pos , size ) ; + + UInt32 features = 0 + | kControlSupportsEmbedding + | kControlSupportsLiveFeedback + | kControlGetsFocusOnClick +// | kControlHasSpecialBackground +// | kControlSupportsCalcBestRect + | kControlHandlesTracking + | kControlSupportsFocus + | kControlWantsActivate + | kControlWantsIdle ; + + m_peer = new wxMacControl(this) ; + OSStatus err =::CreateUserPaneControl( MAC_WXHWND(GetParent()->MacGetTopLevelWindowRef()) , &bounds, features , m_peer->GetControlRefAddr() ); + verify_noerr( err ); + + MacPostControlCreate(pos, size) ; + } + +#ifndef __WXUNIVERSAL__ + // Don't give scrollbars to wxControls unless they ask for them + if ( (! IsKindOf(CLASSINFO(wxControl)) && ! IsKindOf(CLASSINFO(wxStatusBar))) + || (IsKindOf(CLASSINFO(wxControl)) && ((style & wxHSCROLL) || (style & wxVSCROLL)))) + { + MacCreateScrollBars( style ) ; + } +#endif + + wxWindowCreateEvent event(this); + GetEventHandler()->AddPendingEvent(event); + + return true; +} + +void wxWindowMac::MacChildAdded() +{ + if ( m_vScrollBar ) + m_vScrollBar->Raise() ; + if ( m_hScrollBar ) + m_hScrollBar->Raise() ; +} + +void wxWindowMac::MacPostControlCreate(const wxPoint& WXUNUSED(pos), const wxSize& size) +{ + wxASSERT_MSG( m_peer != NULL && m_peer->Ok() , wxT("No valid mac control") ) ; + + m_peer->SetReference( (URefCon) this ) ; + GetParent()->AddChild( this ); + + MacInstallEventHandler( (WXWidget) m_peer->GetControlRef() ); + + ControlRef container = (ControlRef) GetParent()->GetHandle() ; + wxASSERT_MSG( container != NULL , wxT("No valid mac container control") ) ; + ::EmbedControl( m_peer->GetControlRef() , container ) ; + GetParent()->MacChildAdded() ; + + // adjust font, controlsize etc + DoSetWindowVariant( m_windowVariant ) ; + + m_peer->SetLabel( wxStripMenuCodes(m_label, wxStrip_Mnemonics) ) ; + + if (!m_macIsUserPane) + SetInitialSize(size); + + SetCursor( *wxSTANDARD_CURSOR ) ; +} + +void wxWindowMac::DoSetWindowVariant( wxWindowVariant variant ) +{ + // Don't assert, in case we set the window variant before + // the window is created + // wxASSERT( m_peer->Ok() ) ; + + m_windowVariant = variant ; + + if (m_peer == NULL || !m_peer->Ok()) + return; + + ControlSize size ; + ThemeFontID themeFont = kThemeSystemFont ; + + // we will get that from the settings later + // and make this NORMAL later, but first + // we have a few calculations that we must fix + + switch ( variant ) + { + case wxWINDOW_VARIANT_NORMAL : + size = kControlSizeNormal; + themeFont = kThemeSystemFont ; + break ; + + case wxWINDOW_VARIANT_SMALL : + size = kControlSizeSmall; + themeFont = kThemeSmallSystemFont ; + break ; + + case wxWINDOW_VARIANT_MINI : + // not always defined in the headers + size = 3 ; + themeFont = 109 ; + break ; + + case wxWINDOW_VARIANT_LARGE : + size = kControlSizeLarge; + themeFont = kThemeSystemFont ; + break ; + + default: + wxFAIL_MSG(_T("unexpected window variant")); + break ; + } + + m_peer->SetData(kControlEntireControl, kControlSizeTag, &size ) ; + + wxFont font ; + font.MacCreateFromThemeFont( themeFont ) ; + SetFont( font ) ; +} + +void wxWindowMac::MacUpdateControlFont() +{ + m_peer->SetFont( GetFont() , GetForegroundColour() , GetWindowStyle() ) ; + // do not trigger refreshes upon invisible and possible partly created objects + if ( IsShownOnScreen() ) + Refresh() ; +} + +bool wxWindowMac::SetFont(const wxFont& font) +{ + bool retval = wxWindowBase::SetFont( font ); + + MacUpdateControlFont() ; + + return retval; +} + +bool wxWindowMac::SetForegroundColour(const wxColour& col ) +{ + bool retval = wxWindowBase::SetForegroundColour( col ); + + if (retval) + MacUpdateControlFont(); + + return retval; +} + +bool wxWindowMac::SetBackgroundColour(const wxColour& col ) +{ + if ( !wxWindowBase::SetBackgroundColour(col) && m_hasBgCol ) + return false ; + + m_peer->SetBackgroundColour( col ) ; + + return true ; +} + +bool wxWindowMac::MacCanFocus() const +{ + // TODO : evaluate performance hits by looking up this value, eventually cache the results for a 1 sec or so + // CAUTION : the value returned currently is 0 or 2, I've also found values of 1 having the same meaning, + // but the value range is nowhere documented + Boolean keyExistsAndHasValidFormat ; + CFIndex fullKeyboardAccess = CFPreferencesGetAppIntegerValue( CFSTR("AppleKeyboardUIMode" ) , + kCFPreferencesCurrentApplication, &keyExistsAndHasValidFormat ); + + if ( keyExistsAndHasValidFormat && fullKeyboardAccess > 0 ) + { + return true ; + } + else + { + UInt32 features = 0 ; + m_peer->GetFeatures( &features ) ; + + return features & ( kControlSupportsFocus | kControlGetsFocusOnClick ) ; + } +} + +void wxWindowMac::SetFocus() +{ + if ( !AcceptsFocus() ) + return ; + + wxWindow* former = FindFocus() ; + if ( former == this ) + return ; + + // as we cannot rely on the control features to find out whether we are in full keyboard mode, + // we can only leave in case of an error + wxLogTrace(_T("Focus"), _T("before wxWindow::SetFocus(%p) %d"), wx_static_cast(void*, this), GetName().c_str()); + OSStatus err = m_peer->SetFocus( kControlFocusNextPart ) ; + if ( err == errCouldntSetFocus ) + { + wxLogTrace(_T("Focus"), _T("in wxWindow::SetFocus(%p) errCouldntSetFocus"), wx_static_cast(void*, this)); + return ; + } + wxLogTrace(_T("Focus"), _T("after wxWindow::SetFocus(%p)"), wx_static_cast(void*, this)); + + SetUserFocusWindow( (WindowRef)MacGetTopLevelWindowRef() ); +} + +void wxWindowMac::DoCaptureMouse() +{ + wxApp::s_captureWindow = this ; +} + +wxWindow * wxWindowBase::GetCapture() +{ + return wxApp::s_captureWindow ; +} + +void wxWindowMac::DoReleaseMouse() +{ + wxApp::s_captureWindow = NULL ; +} + +#if wxUSE_DRAG_AND_DROP + +void wxWindowMac::SetDropTarget(wxDropTarget *pDropTarget) +{ + if ( m_dropTarget != NULL ) + delete m_dropTarget; + + m_dropTarget = pDropTarget; + if ( m_dropTarget != NULL ) + { + // TODO: + } +} + +#endif + +// Old-style File Manager Drag & Drop +void wxWindowMac::DragAcceptFiles(bool WXUNUSED(accept)) +{ + // TODO: +} + +// Returns the size of the native control. In the case of the toplevel window +// this is the content area root control + +void wxWindowMac::MacGetPositionAndSizeFromControl(int& WXUNUSED(x), + int& WXUNUSED(y), + int& WXUNUSED(w), + int& WXUNUSED(h)) const +{ + wxFAIL_MSG( wxT("Not currently supported") ) ; +} + +// From a wx position / size calculate the appropriate size of the native control + +bool wxWindowMac::MacGetBoundsForControl( + const wxPoint& pos, + const wxSize& size, + int& x, int& y, + int& w, int& h , bool adjustOrigin ) const +{ + // the desired size, minus the border pixels gives the correct size of the control + x = (int)pos.x; + y = (int)pos.y; + + // TODO: the default calls may be used as soon as PostCreateControl Is moved here + w = wxMax(size.x, 0) ; // WidthDefault( size.x ); + h = wxMax(size.y, 0) ; // HeightDefault( size.y ) ; + + x += MacGetLeftBorderSize() ; + y += MacGetTopBorderSize() ; + w -= MacGetLeftBorderSize() + MacGetRightBorderSize() ; + h -= MacGetTopBorderSize() + MacGetBottomBorderSize() ; + + if ( adjustOrigin ) + AdjustForParentClientOrigin( x , y ) ; + + // this is in window relative coordinate, as this parent may have a border, its physical position is offset by this border + if ( !GetParent()->IsTopLevel() ) + { + x -= GetParent()->MacGetLeftBorderSize() ; + y -= GetParent()->MacGetTopBorderSize() ; + } + + return true ; +} + +// Get window size (not client size) +void wxWindowMac::DoGetSize(int *x, int *y) const +{ + Rect bounds ; + m_peer->GetRect( &bounds ) ; + + if (x) + *x = bounds.right - bounds.left + MacGetLeftBorderSize() + MacGetRightBorderSize() ; + if (y) + *y = bounds.bottom - bounds.top + MacGetTopBorderSize() + MacGetBottomBorderSize() ; +} + +// get the position of the bounds of this window in client coordinates of its parent +void wxWindowMac::DoGetPosition(int *x, int *y) const +{ + Rect bounds ; + m_peer->GetRect( &bounds ) ; + + int x1 = bounds.left ; + int y1 = bounds.top ; + + // get the wx window position from the native one + x1 -= MacGetLeftBorderSize() ; + y1 -= MacGetTopBorderSize() ; + + if ( !IsTopLevel() ) + { + wxWindow *parent = GetParent(); + if ( parent ) + { + // we must first adjust it to be in window coordinates of the parent, + // as otherwise it gets lost by the ClientAreaOrigin fix + x1 += parent->MacGetLeftBorderSize() ; + y1 += parent->MacGetTopBorderSize() ; + + // and now to client coordinates + wxPoint pt(parent->GetClientAreaOrigin()); + x1 -= pt.x ; + y1 -= pt.y ; + } + } + + if (x) + *x = x1 ; + if (y) + *y = y1 ; +} + +void wxWindowMac::DoScreenToClient(int *x, int *y) const +{ + WindowRef window = (WindowRef) MacGetTopLevelWindowRef() ; + wxCHECK_RET( window , wxT("TopLevel Window missing") ) ; + + Point localwhere = { 0, 0 } ; + + if (x) + localwhere.h = *x ; + if (y) + localwhere.v = *y ; + + wxMacGlobalToLocal( window , &localwhere ) ; + + if (x) + *x = localwhere.h ; + if (y) + *y = localwhere.v ; + + MacRootWindowToWindow( x , y ) ; + + wxPoint origin = GetClientAreaOrigin() ; + if (x) + *x -= origin.x ; + if (y) + *y -= origin.y ; +} + +void wxWindowMac::DoClientToScreen(int *x, int *y) const +{ + WindowRef window = (WindowRef) MacGetTopLevelWindowRef() ; + wxCHECK_RET( window , wxT("TopLevel window missing") ) ; + + wxPoint origin = GetClientAreaOrigin() ; + if (x) + *x += origin.x ; + if (y) + *y += origin.y ; + + MacWindowToRootWindow( x , y ) ; + + Point localwhere = { 0, 0 }; + if (x) + localwhere.h = *x ; + if (y) + localwhere.v = *y ; + + wxMacLocalToGlobal( window, &localwhere ) ; + + if (x) + *x = localwhere.h ; + if (y) + *y = localwhere.v ; +} + +void wxWindowMac::MacClientToRootWindow( int *x , int *y ) const +{ + wxPoint origin = GetClientAreaOrigin() ; + if (x) + *x += origin.x ; + if (y) + *y += origin.y ; + + MacWindowToRootWindow( x , y ) ; +} + +void wxWindowMac::MacRootWindowToClient( int *x , int *y ) const +{ + MacRootWindowToWindow( x , y ) ; + + wxPoint origin = GetClientAreaOrigin() ; + if (x) + *x -= origin.x ; + if (y) + *y -= origin.y ; +} + +void wxWindowMac::MacWindowToRootWindow( int *x , int *y ) const +{ + wxPoint pt ; + + if (x) + pt.x = *x ; + if (y) + pt.y = *y ; + + if ( !IsTopLevel() ) + { + wxNonOwnedWindow* top = MacGetTopLevelWindow(); + if (top) + { + pt.x -= MacGetLeftBorderSize() ; + pt.y -= MacGetTopBorderSize() ; + wxMacControl::Convert( &pt , m_peer , top->m_peer ) ; + } + } + + if (x) + *x = (int) pt.x ; + if (y) + *y = (int) pt.y ; +} + +void wxWindowMac::MacWindowToRootWindow( short *x , short *y ) const +{ + int x1 , y1 ; + + if (x) + x1 = *x ; + if (y) + y1 = *y ; + + MacWindowToRootWindow( &x1 , &y1 ) ; + + if (x) + *x = x1 ; + if (y) + *y = y1 ; +} + +void wxWindowMac::MacRootWindowToWindow( int *x , int *y ) const +{ + wxPoint pt ; + + if (x) + pt.x = *x ; + if (y) + pt.y = *y ; + + if ( !IsTopLevel() ) + { + wxNonOwnedWindow* top = MacGetTopLevelWindow(); + if (top) + { + wxMacControl::Convert( &pt , top->m_peer , m_peer ) ; + pt.x += MacGetLeftBorderSize() ; + pt.y += MacGetTopBorderSize() ; + } + } + + if (x) + *x = (int) pt.x ; + if (y) + *y = (int) pt.y ; +} + +void wxWindowMac::MacRootWindowToWindow( short *x , short *y ) const +{ + int x1 , y1 ; + + if (x) + x1 = *x ; + if (y) + y1 = *y ; + + MacRootWindowToWindow( &x1 , &y1 ) ; + + if (x) + *x = x1 ; + if (y) + *y = y1 ; +} + +void wxWindowMac::MacGetContentAreaInset( int &left , int &top , int &right , int &bottom ) +{ + RgnHandle rgn = NewRgn() ; + + if ( m_peer->GetRegion( kControlContentMetaPart , rgn ) == noErr ) + { + Rect structure, content ; + + GetRegionBounds( rgn , &content ) ; + m_peer->GetRect( &structure ) ; + OffsetRect( &structure, -structure.left , -structure.top ) ; + + left = content.left - structure.left ; + top = content.top - structure.top ; + right = structure.right - content.right ; + bottom = structure.bottom - content.bottom ; + } + else + { + left = top = right = bottom = 0 ; + } + + DisposeRgn( rgn ) ; +} + +wxSize wxWindowMac::DoGetSizeFromClientSize( const wxSize & size ) const +{ + wxSize sizeTotal = size; + + RgnHandle rgn = NewRgn() ; + if ( m_peer->GetRegion( kControlContentMetaPart , rgn ) == noErr ) + { + Rect content, structure ; + GetRegionBounds( rgn , &content ) ; + m_peer->GetRect( &structure ) ; + + // structure is in parent coordinates, but we only need width and height, so it's ok + + sizeTotal.x += (structure.right - structure.left) - (content.right - content.left) ; + sizeTotal.y += (structure.bottom - structure.top) - (content.bottom - content.top) ; + } + + DisposeRgn( rgn ) ; + + sizeTotal.x += MacGetLeftBorderSize() + MacGetRightBorderSize() ; + sizeTotal.y += MacGetTopBorderSize() + MacGetBottomBorderSize() ; + + return sizeTotal; +} + +// Get size *available for subwindows* i.e. excluding menu bar etc. +void wxWindowMac::DoGetClientSize( int *x, int *y ) const +{ + int ww, hh; + + RgnHandle rgn = NewRgn() ; + Rect content ; + if ( m_peer->GetRegion( kControlContentMetaPart , rgn ) == noErr ) + GetRegionBounds( rgn , &content ) ; + else + m_peer->GetRect( &content ) ; + DisposeRgn( rgn ) ; + + ww = content.right - content.left ; + hh = content.bottom - content.top ; + + if (m_hScrollBar && m_hScrollBar->IsShown() ) + hh -= m_hScrollBar->GetSize().y ; + + if (m_vScrollBar && m_vScrollBar->IsShown() ) + ww -= m_vScrollBar->GetSize().x ; + + if (x) + *x = ww; + if (y) + *y = hh; +} + +bool wxWindowMac::SetCursor(const wxCursor& cursor) +{ + if (m_cursor.IsSameAs(cursor)) + return false; + + if (!cursor.IsOk()) + { + if ( ! wxWindowBase::SetCursor( *wxSTANDARD_CURSOR ) ) + return false ; + } + else + { + if ( ! wxWindowBase::SetCursor( cursor ) ) + return false ; + } + + wxASSERT_MSG( m_cursor.Ok(), + wxT("cursor must be valid after call to the base version")); + + wxWindowMac *mouseWin = 0 ; + { + wxNonOwnedWindow *tlw = MacGetTopLevelWindow() ; + WindowRef window = (WindowRef) ( tlw ? tlw->MacGetWindowRef() : 0 ) ; + + ControlPartCode part ; + ControlRef control ; + Point pt ; + #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + HIPoint hiPoint ; + HIGetMousePosition(kHICoordSpaceWindow, window, &hiPoint); + pt.h = hiPoint.x; + pt.v = hiPoint.y; + #else + GetGlobalMouse( &pt ); + int x = pt.h; + int y = pt.v; + ScreenToClient(&x, &y); + pt.h = x; + pt.v = y; +#endif + control = FindControlUnderMouse( pt , window , &part ) ; + if ( control ) + mouseWin = wxFindControlFromMacControl( control ) ; + + } + + if ( mouseWin == this && !wxIsBusy() ) + m_cursor.MacInstall() ; + + return true ; +} + +#if wxUSE_MENUS +bool wxWindowMac::DoPopupMenu(wxMenu *menu, int x, int y) +{ +#ifndef __WXUNIVERSAL__ + menu->SetInvokingWindow((wxWindow*)this); + menu->UpdateUI(); + + if ( x == wxDefaultCoord && y == wxDefaultCoord ) + { + wxPoint mouse = wxGetMousePosition(); + x = mouse.x; + y = mouse.y; + } + else + { + ClientToScreen( &x , &y ) ; + } + + menu->MacBeforeDisplay( true ) ; + long menuResult = ::PopUpMenuSelect((MenuHandle) menu->GetHMenu() , y, x, 0) ; + if ( HiWord(menuResult) != 0 ) + { + MenuCommand macid; + GetMenuItemCommandID( GetMenuHandle(HiWord(menuResult)) , LoWord(menuResult) , &macid ); + int id = wxMacCommandToId( macid ); + wxMenuItem* item = NULL ; + wxMenu* realmenu ; + item = menu->FindItem( id, &realmenu ) ; + if ( item ) + { + if (item->IsCheckable()) + item->Check( !item->IsChecked() ) ; + + menu->SendEvent( id , item->IsCheckable() ? item->IsChecked() : -1 ) ; + } + } + + menu->MacAfterDisplay( true ) ; + menu->SetInvokingWindow( NULL ); + + return true; +#else + // actually this shouldn't be called, because universal is having its own implementation + return false; +#endif +} +#endif + +// ---------------------------------------------------------------------------- +// tooltips +// ---------------------------------------------------------------------------- + +#if wxUSE_TOOLTIPS + +void wxWindowMac::DoSetToolTip(wxToolTip *tooltip) +{ + wxWindowBase::DoSetToolTip(tooltip); + + if ( m_tooltip ) + m_tooltip->SetWindow(this); +} + +#endif + +void wxWindowMac::MacInvalidateBorders() +{ + if ( m_peer == NULL ) + return ; + + bool vis = IsShownOnScreen() ; + if ( !vis ) + return ; + + int outerBorder = MacGetLeftBorderSize() ; + if ( m_peer->NeedsFocusRect() /* && m_peer->HasFocus() */ ) + outerBorder += 4 ; + + if ( outerBorder == 0 ) + return ; + + // now we know that we have something to do at all + + // as the borders are drawn on the parent we have to properly invalidate all these areas + RgnHandle updateInner , updateOuter; + Rect rect ; + + // this rectangle is in HIViewCoordinates under OSX and in Window Coordinates under Carbon + updateInner = NewRgn() ; + updateOuter = NewRgn() ; + + m_peer->GetRect( &rect ) ; + RectRgn( updateInner, &rect ) ; + InsetRect( &rect , -outerBorder , -outerBorder ) ; + RectRgn( updateOuter, &rect ) ; + DiffRgn( updateOuter, updateInner , updateOuter ) ; + + GetParent()->m_peer->SetNeedsDisplay( updateOuter ) ; + + DisposeRgn( updateOuter ) ; + DisposeRgn( updateInner ) ; +} + +void wxWindowMac::DoMoveWindow(int x, int y, int width, int height) +{ + // this is never called for a toplevel window, so we know we have a parent + int former_x , former_y , former_w, former_h ; + + // Get true coordinates of former position + DoGetPosition( &former_x , &former_y ) ; + DoGetSize( &former_w , &former_h ) ; + + wxWindow *parent = GetParent(); + if ( parent ) + { + wxPoint pt(parent->GetClientAreaOrigin()); + former_x += pt.x ; + former_y += pt.y ; + } + + int actualWidth = width ; + int actualHeight = height ; + int actualX = x; + int actualY = y; + + if ((m_minWidth != -1) && (actualWidth < m_minWidth)) + actualWidth = m_minWidth; + if ((m_minHeight != -1) && (actualHeight < m_minHeight)) + actualHeight = m_minHeight; + if ((m_maxWidth != -1) && (actualWidth > m_maxWidth)) + actualWidth = m_maxWidth; + if ((m_maxHeight != -1) && (actualHeight > m_maxHeight)) + actualHeight = m_maxHeight; + + bool doMove = false, doResize = false ; + + if ( actualX != former_x || actualY != former_y ) + doMove = true ; + + if ( actualWidth != former_w || actualHeight != former_h ) + doResize = true ; + + if ( doMove || doResize ) + { + // as the borders are drawn outside the native control, we adjust now + + wxRect bounds( wxPoint( actualX + MacGetLeftBorderSize() ,actualY + MacGetTopBorderSize() ), + wxSize( actualWidth - (MacGetLeftBorderSize() + MacGetRightBorderSize()) , + actualHeight - (MacGetTopBorderSize() + MacGetBottomBorderSize()) ) ) ; + + Rect r ; + wxMacRectToNative( &bounds , &r ) ; + + if ( !GetParent()->IsTopLevel() ) + wxMacWindowToNative( GetParent() , &r ) ; + + MacInvalidateBorders() ; + + m_cachedClippedRectValid = false ; + m_peer->SetRect( &r ) ; + + wxWindowMac::MacSuperChangedPosition() ; // like this only children will be notified + + MacInvalidateBorders() ; + + MacRepositionScrollBars() ; + if ( doMove ) + { + wxPoint point(actualX, actualY); + wxMoveEvent event(point, m_windowId); + event.SetEventObject(this); + HandleWindowEvent(event) ; + } + + if ( doResize ) + { + MacRepositionScrollBars() ; + wxSize size(actualWidth, actualHeight); + wxSizeEvent event(size, m_windowId); + event.SetEventObject(this); + HandleWindowEvent(event); + } + } +} + +wxSize wxWindowMac::DoGetBestSize() const +{ + if ( m_macIsUserPane || IsTopLevel() ) + { + return wxWindowBase::DoGetBestSize() ; + } + else + { + Rect bestsize = { 0 , 0 , 0 , 0 } ; + int bestWidth, bestHeight ; + + m_peer->GetBestRect( &bestsize ) ; + if ( EmptyRect( &bestsize ) ) + { + bestsize.left = + bestsize.top = 0 ; + bestsize.right = + bestsize.bottom = 16 ; + + if ( IsKindOf( CLASSINFO( wxScrollBar ) ) ) + { + bestsize.bottom = 16 ; + } + #if wxUSE_SPINBTN + else if ( IsKindOf( CLASSINFO( wxSpinButton ) ) ) + { + bestsize.bottom = 24 ; + } + #endif + else + { + // return wxWindowBase::DoGetBestSize() ; + } + } + + bestWidth = bestsize.right - bestsize.left + MacGetLeftBorderSize() + + MacGetRightBorderSize(); + bestHeight = bestsize.bottom - bestsize.top + MacGetTopBorderSize() + + MacGetBottomBorderSize(); + if ( bestHeight < 10 ) + bestHeight = 13 ; + + return wxSize(bestWidth, bestHeight); + } +} + +// set the size of the window: if the dimensions are positive, just use them, +// but if any of them is equal to -1, it means that we must find the value for +// it ourselves (unless sizeFlags contains wxSIZE_ALLOW_MINUS_ONE flag, in +// which case -1 is a valid value for x and y) +// +// If sizeFlags contains wxSIZE_AUTO_WIDTH/HEIGHT flags (default), we calculate +// the width/height to best suit our contents, otherwise we reuse the current +// width/height +void wxWindowMac::DoSetSize(int x, int y, int width, int height, int sizeFlags) +{ + // get the current size and position... + int currentX, currentY; + int currentW, currentH; + + GetPosition(¤tX, ¤tY); + GetSize(¤tW, ¤tH); + + // ... and don't do anything (avoiding flicker) if it's already ok + if ( x == currentX && y == currentY && + width == currentW && height == currentH && ( height != -1 && width != -1 ) ) + { + // TODO: REMOVE + MacRepositionScrollBars() ; // we might have a real position shift + + return; + } + + if ( !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) ) + { + if ( x == wxDefaultCoord ) + x = currentX; + if ( y == wxDefaultCoord ) + y = currentY; + } + + AdjustForParentClientOrigin( x, y, sizeFlags ); + + wxSize size = wxDefaultSize; + if ( width == wxDefaultCoord ) + { + if ( sizeFlags & wxSIZE_AUTO_WIDTH ) + { + size = DoGetBestSize(); + width = size.x; + } + else + { + // just take the current one + width = currentW; + } + } + + if ( height == wxDefaultCoord ) + { + if ( sizeFlags & wxSIZE_AUTO_HEIGHT ) + { + if ( size.x == wxDefaultCoord ) + size = DoGetBestSize(); + // else: already called DoGetBestSize() above + + height = size.y; + } + else + { + // just take the current one + height = currentH; + } + } + + DoMoveWindow( x, y, width, height ); +} + +wxPoint wxWindowMac::GetClientAreaOrigin() const +{ + RgnHandle rgn = NewRgn() ; + Rect content ; + if ( m_peer->GetRegion( kControlContentMetaPart , rgn ) == noErr ) + { + GetRegionBounds( rgn , &content ) ; + } + else + { + content.left = + content.top = 0 ; + } + + DisposeRgn( rgn ) ; + + return wxPoint( content.left + MacGetLeftBorderSize() , content.top + MacGetTopBorderSize() ); +} + +void wxWindowMac::DoSetClientSize(int clientwidth, int clientheight) +{ + if ( clientwidth != wxDefaultCoord || clientheight != wxDefaultCoord ) + { + int currentclientwidth , currentclientheight ; + int currentwidth , currentheight ; + + GetClientSize( ¤tclientwidth , ¤tclientheight ) ; + GetSize( ¤twidth , ¤theight ) ; + + DoSetSize( wxDefaultCoord , wxDefaultCoord , currentwidth + clientwidth - currentclientwidth , + currentheight + clientheight - currentclientheight , wxSIZE_USE_EXISTING ) ; + } +} + +void wxWindowMac::SetLabel(const wxString& title) +{ + m_label = title ; + + if ( m_peer && m_peer->Ok() ) + m_peer->SetLabel( wxStripMenuCodes(m_label, wxStrip_Mnemonics) ) ; + + // do not trigger refreshes upon invisible and possible partly created objects + if ( IsShownOnScreen() ) + Refresh() ; +} + +wxString wxWindowMac::GetLabel() const +{ + return m_label ; +} + +bool wxWindowMac::Show(bool show) +{ + if ( !wxWindowBase::Show(show) ) + return false; + + if ( m_peer ) + m_peer->SetVisibility( show , true ) ; + + return true; +} + +void wxWindowMac::DoEnable(bool enable) +{ + m_peer->Enable( enable ) ; +} + +// +// status change notifications +// + +void wxWindowMac::MacVisibilityChanged() +{ +} + +void wxWindowMac::MacHiliteChanged() +{ +} + +void wxWindowMac::MacEnabledStateChanged() +{ + OnEnabled( m_peer->IsEnabled() ); +} + +// +// status queries on the inherited window's state +// + +bool wxWindowMac::MacIsReallyEnabled() +{ + return m_peer->IsEnabled() ; +} + +bool wxWindowMac::MacIsReallyHilited() +{ + return m_peer->IsActive(); +} + +void wxWindowMac::MacFlashInvalidAreas() +{ +#if TARGET_API_MAC_OSX + HIViewFlashDirtyArea( (WindowRef) MacGetTopLevelWindowRef() ) ; +#endif +} + +int wxWindowMac::GetCharHeight() const +{ + wxClientDC dc( (wxWindowMac*)this ) ; + + return dc.GetCharHeight() ; +} + +int wxWindowMac::GetCharWidth() const +{ + wxClientDC dc( (wxWindowMac*)this ) ; + + return dc.GetCharWidth() ; +} + +void wxWindowMac::GetTextExtent(const wxString& string, int *x, int *y, + int *descent, int *externalLeading, const wxFont *theFont ) const +{ + const wxFont *fontToUse = theFont; + wxFont tempFont; + if ( !fontToUse ) + { + tempFont = GetFont(); + fontToUse = &tempFont; + } + + wxClientDC dc( (wxWindowMac*) this ) ; + wxCoord lx,ly,ld,le ; + dc.GetTextExtent( string , &lx , &ly , &ld, &le, (wxFont *)fontToUse ) ; + if ( externalLeading ) + *externalLeading = le ; + if ( descent ) + *descent = ld ; + if ( x ) + *x = lx ; + if ( y ) + *y = ly ; +} + +/* + * Rect is given in client coordinates, for further reading, read wxTopLevelWindowMac::InvalidateRect + * we always intersect with the entire window, not only with the client area + */ + +void wxWindowMac::Refresh(bool WXUNUSED(eraseBack), const wxRect *rect) +{ + if ( m_peer == NULL ) + return ; + + if ( !IsShownOnScreen() ) + return ; + + if ( rect ) + { + Rect r ; + + wxMacRectToNative( rect , &r ) ; + m_peer->SetNeedsDisplay( &r ) ; + } + else + { + m_peer->SetNeedsDisplay() ; + } +} + +void wxWindowMac::DoFreeze() +{ +#if TARGET_API_MAC_OSX + if ( m_peer && m_peer->Ok() ) + m_peer->SetDrawingEnabled( false ) ; +#endif +} + +void wxWindowMac::DoThaw() +{ +#if TARGET_API_MAC_OSX + if ( m_peer && m_peer->Ok() ) + { + m_peer->SetDrawingEnabled( true ) ; + m_peer->InvalidateWithChildren() ; + } +#endif +} + +wxWindowMac *wxGetActiveWindow() +{ + // actually this is a windows-only concept + return NULL; +} + +// Coordinates relative to the window +void wxWindowMac::WarpPointer(int WXUNUSED(x_pos), int WXUNUSED(y_pos)) +{ + // We really don't move the mouse programmatically under Mac. +} + +void wxWindowMac::OnEraseBackground(wxEraseEvent& event) +{ + if ( MacGetTopLevelWindow() == NULL ) + return ; +/* +#if TARGET_API_MAC_OSX + if ( !m_backgroundColour.Ok() || GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT ) + { + } + else +#endif +*/ + if ( GetBackgroundStyle() == wxBG_STYLE_COLOUR ) + { + event.GetDC()->Clear() ; + } + else if ( GetBackgroundStyle() == wxBG_STYLE_CUSTOM ) + { + // don't skip the event here, custom background means that the app + // is drawing it itself in its OnPaint(), so don't draw it at all + // now to avoid flicker + } + else + { + event.Skip() ; + } +} + +void wxWindowMac::OnNcPaint( wxNcPaintEvent& event ) +{ + event.Skip() ; +} + +int wxWindowMac::GetScrollPos(int orient) const +{ + if ( orient == wxHORIZONTAL ) + { + if ( m_hScrollBar ) + return m_hScrollBar->GetThumbPosition() ; + } + else + { + if ( m_vScrollBar ) + return m_vScrollBar->GetThumbPosition() ; + } + + return 0; +} + +// This now returns the whole range, not just the number +// of positions that we can scroll. +int wxWindowMac::GetScrollRange(int orient) const +{ + if ( orient == wxHORIZONTAL ) + { + if ( m_hScrollBar ) + return m_hScrollBar->GetRange() ; + } + else + { + if ( m_vScrollBar ) + return m_vScrollBar->GetRange() ; + } + + return 0; +} + +int wxWindowMac::GetScrollThumb(int orient) const +{ + if ( orient == wxHORIZONTAL ) + { + if ( m_hScrollBar ) + return m_hScrollBar->GetThumbSize() ; + } + else + { + if ( m_vScrollBar ) + return m_vScrollBar->GetThumbSize() ; + } + + return 0; +} + +void wxWindowMac::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh)) +{ + if ( orient == wxHORIZONTAL ) + { + if ( m_hScrollBar ) + m_hScrollBar->SetThumbPosition( pos ) ; + } + else + { + if ( m_vScrollBar ) + m_vScrollBar->SetThumbPosition( pos ) ; + } +} + +void +wxWindowMac::AlwaysShowScrollbars(bool hflag, bool vflag) +{ + bool needVisibilityUpdate = false; + + if ( m_hScrollBarAlwaysShown != hflag ) + { + m_hScrollBarAlwaysShown = hflag; + needVisibilityUpdate = true; + } + + if ( m_vScrollBarAlwaysShown != vflag ) + { + m_vScrollBarAlwaysShown = vflag; + needVisibilityUpdate = true; + } + + if ( needVisibilityUpdate ) + DoUpdateScrollbarVisibility(); +} + +// +// we draw borders and grow boxes, are already set up and clipped in the current port / cgContextRef +// our own window origin is at leftOrigin/rightOrigin +// + +void wxWindowMac::MacPaintGrowBox() +{ + if ( IsTopLevel() ) + return ; + + if ( MacHasScrollBarCorner() ) + { + Rect rect ; + + CGContextRef cgContext = (CGContextRef) MacGetCGContextRef() ; + wxASSERT( cgContext ) ; + + m_peer->GetRect( &rect ) ; + + int size = m_hScrollBar ? m_hScrollBar->GetSize().y : ( m_vScrollBar ? m_vScrollBar->GetSize().x : MAC_SCROLLBAR_SIZE ) ; + CGRect cgrect = CGRectMake( rect.right - size , rect.bottom - size , size , size ) ; + CGPoint cgpoint = CGPointMake( rect.right - size , rect.bottom - size ) ; + CGContextSaveGState( cgContext ); + + if ( m_backgroundColour.Ok() ) + { + CGContextSetFillColorWithColor( cgContext, m_backgroundColour.GetCGColor() ); + } + else + { + CGContextSetRGBFillColor( cgContext, (CGFloat) 1.0, (CGFloat)1.0 ,(CGFloat) 1.0 , (CGFloat)1.0 ); + } + CGContextFillRect( cgContext, cgrect ); + CGContextRestoreGState( cgContext ); + } +} + +void wxWindowMac::MacPaintBorders( int WXUNUSED(leftOrigin) , int WXUNUSED(rightOrigin) ) +{ + if ( IsTopLevel() ) + return ; + + Rect rect ; + bool hasFocus = m_peer->NeedsFocusRect() && m_peer->HasFocus() ; + + // back to the surrounding frame rectangle + m_peer->GetRect( &rect ) ; + InsetRect( &rect, -1 , -1 ) ; + + { + CGRect cgrect = CGRectMake( rect.left , rect.top , rect.right - rect.left , + rect.bottom - rect.top ) ; + + HIThemeFrameDrawInfo info ; + memset( &info, 0 , sizeof(info) ) ; + + info.version = 0 ; + info.kind = 0 ; + info.state = IsEnabled() ? kThemeStateActive : kThemeStateInactive ; + info.isFocused = hasFocus ; + + CGContextRef cgContext = (CGContextRef) GetParent()->MacGetCGContextRef() ; + wxASSERT( cgContext ) ; + + if ( HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER) || HasFlag(wxDOUBLE_BORDER) ) + { + info.kind = kHIThemeFrameTextFieldSquare ; + HIThemeDrawFrame( &cgrect , &info , cgContext , kHIThemeOrientationNormal ) ; + } + else if ( HasFlag(wxSIMPLE_BORDER) ) + { + info.kind = kHIThemeFrameListBox ; + HIThemeDrawFrame( &cgrect , &info , cgContext , kHIThemeOrientationNormal ) ; + } + else if ( hasFocus ) + { + HIThemeDrawFocusRect( &cgrect , true , cgContext , kHIThemeOrientationNormal ) ; + } +#if 0 // TODO REMOVE now done in a separate call earlier in drawing the window itself + m_peer->GetRect( &rect ) ; + if ( MacHasScrollBarCorner() ) + { + int variant = (m_hScrollBar == NULL ? m_vScrollBar : m_hScrollBar ) ->GetWindowVariant(); + int size = m_hScrollBar ? m_hScrollBar->GetSize().y : ( m_vScrollBar ? m_vScrollBar->GetSize().x : MAC_SCROLLBAR_SIZE ) ; + CGRect cgrect = CGRectMake( rect.right - size , rect.bottom - size , size , size ) ; + CGPoint cgpoint = CGPointMake( rect.right - size , rect.bottom - size ) ; + HIThemeGrowBoxDrawInfo info ; + memset( &info, 0, sizeof(info) ) ; + info.version = 0 ; + info.state = IsEnabled() ? kThemeStateActive : kThemeStateInactive ; + info.kind = kHIThemeGrowBoxKindNone ; + // contrary to the docs ...SizeSmall does not work + info.size = kHIThemeGrowBoxSizeNormal ; + info.direction = 0 ; + HIThemeDrawGrowBox( &cgpoint , &info , cgContext , kHIThemeOrientationNormal ) ; + } +#endif + } +} + +void wxWindowMac::RemoveChild( wxWindowBase *child ) +{ + if ( child == m_hScrollBar ) + m_hScrollBar = NULL ; + if ( child == m_vScrollBar ) + m_vScrollBar = NULL ; + + wxWindowBase::RemoveChild( child ) ; +} + +void wxWindowMac::DoUpdateScrollbarVisibility() +{ + bool triggerSizeEvent = false; + + if ( m_hScrollBar ) + { + bool showHScrollBar = m_hScrollBarAlwaysShown || m_hScrollBar->IsNeeded(); + + if ( m_hScrollBar->IsShown() != showHScrollBar ) + { + m_hScrollBar->Show( showHScrollBar ); + triggerSizeEvent = true; + } + } + + if ( m_vScrollBar) + { + bool showVScrollBar = m_vScrollBarAlwaysShown || m_vScrollBar->IsNeeded(); + + if ( m_vScrollBar->IsShown() != showVScrollBar ) + { + m_vScrollBar->Show( showVScrollBar ) ; + triggerSizeEvent = true; + } + } + + MacRepositionScrollBars() ; + if ( triggerSizeEvent ) + { + wxSizeEvent event(GetSize(), m_windowId); + event.SetEventObject(this); + HandleWindowEvent(event); + } +} + +// New function that will replace some of the above. +void wxWindowMac::SetScrollbar(int orient, int pos, int thumb, + int range, bool refresh) +{ + if ( orient == wxHORIZONTAL && m_hScrollBar ) + m_hScrollBar->SetScrollbar(pos, thumb, range, thumb, refresh); + else if ( orient == wxVERTICAL && m_vScrollBar ) + m_vScrollBar->SetScrollbar(pos, thumb, range, thumb, refresh); + + DoUpdateScrollbarVisibility(); +} + +// Does a physical scroll +void wxWindowMac::ScrollWindow(int dx, int dy, const wxRect *rect) +{ + if ( dx == 0 && dy == 0 ) + return ; + + int width , height ; + GetClientSize( &width , &height ) ; + + { + // note there currently is a bug in OSX which makes inefficient refreshes in case an entire control + // area is scrolled, this does not occur if width and height are 2 pixels less, + // TODO: write optimal workaround + wxRect scrollrect( MacGetLeftBorderSize() , MacGetTopBorderSize() , width , height ) ; + if ( rect ) + scrollrect.Intersect( *rect ) ; + + if ( m_peer->GetNeedsDisplay() ) + { + // because HIViewScrollRect does not scroll the already invalidated area we have two options: + // in case there is already a pending redraw on that area + // either immediate redraw or full invalidate +#if 1 + // is the better overall solution, as it does not slow down scrolling + m_peer->SetNeedsDisplay() ; +#else + // this would be the preferred version for fast drawing controls + HIViewRender(m_peer->GetControlRef()) ; +#endif + } + + // as the native control might be not a 0/0 wx window coordinates, we have to offset + scrollrect.Offset( -MacGetLeftBorderSize() , -MacGetTopBorderSize() ) ; + m_peer->ScrollRect( &scrollrect , dx , dy ) ; + +#if 0 + // this would be the preferred version for fast drawing controls + HIViewRender(m_peer->GetControlRef()) ; +#endif + } + + wxWindowMac *child; + int x, y, w, h; + for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); node; node = node->GetNext()) + { + child = node->GetData(); + if (child == NULL) + continue; + if (child == m_vScrollBar) + continue; + if (child == m_hScrollBar) + continue; + if (child->IsTopLevel()) + continue; + + child->GetPosition( &x, &y ); + child->GetSize( &w, &h ); + if (rect) + { + wxRect rc( x, y, w, h ); + if (rect->Intersects( rc )) + child->SetSize( x + dx, y + dy, w, h, wxSIZE_AUTO|wxSIZE_ALLOW_MINUS_ONE ); + } + else + { + child->SetSize( x + dx, y + dy, w, h, wxSIZE_AUTO|wxSIZE_ALLOW_MINUS_ONE ); + } + } +} + +void wxWindowMac::MacOnScroll( wxScrollEvent &event ) +{ + if ( event.GetEventObject() == m_vScrollBar || event.GetEventObject() == m_hScrollBar ) + { + wxScrollWinEvent wevent; + wevent.SetPosition(event.GetPosition()); + wevent.SetOrientation(event.GetOrientation()); + wevent.SetEventObject(this); + + if (event.GetEventType() == wxEVT_SCROLL_TOP) + wevent.SetEventType( wxEVT_SCROLLWIN_TOP ); + else if (event.GetEventType() == wxEVT_SCROLL_BOTTOM) + wevent.SetEventType( wxEVT_SCROLLWIN_BOTTOM ); + else if (event.GetEventType() == wxEVT_SCROLL_LINEUP) + wevent.SetEventType( wxEVT_SCROLLWIN_LINEUP ); + else if (event.GetEventType() == wxEVT_SCROLL_LINEDOWN) + wevent.SetEventType( wxEVT_SCROLLWIN_LINEDOWN ); + else if (event.GetEventType() == wxEVT_SCROLL_PAGEUP) + wevent.SetEventType( wxEVT_SCROLLWIN_PAGEUP ); + else if (event.GetEventType() == wxEVT_SCROLL_PAGEDOWN) + wevent.SetEventType( wxEVT_SCROLLWIN_PAGEDOWN ); + else if (event.GetEventType() == wxEVT_SCROLL_THUMBTRACK) + wevent.SetEventType( wxEVT_SCROLLWIN_THUMBTRACK ); + else if (event.GetEventType() == wxEVT_SCROLL_THUMBRELEASE) + wevent.SetEventType( wxEVT_SCROLLWIN_THUMBRELEASE ); + + HandleWindowEvent(wevent); + } +} + +// Get the window with the focus +wxWindowMac *wxWindowBase::DoFindFocus() +{ + ControlRef control ; + GetKeyboardFocus( GetUserFocusWindow() , &control ) ; + return wxFindControlFromMacControl( control ) ; +} + +void wxWindowMac::OnInternalIdle() +{ + // This calls the UI-update mechanism (querying windows for + // menu/toolbar/control state information) + if (wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen()) + UpdateWindowUI(wxUPDATE_UI_FROMIDLE); +} + +// Raise the window to the top of the Z order +void wxWindowMac::Raise() +{ + m_peer->SetZOrder( true , NULL ) ; +} + +// Lower the window to the bottom of the Z order +void wxWindowMac::Lower() +{ + m_peer->SetZOrder( false , NULL ) ; +} + +// static wxWindow *gs_lastWhich = NULL; + +bool wxWindowMac::MacSetupCursor( const wxPoint& pt ) +{ + // first trigger a set cursor event + + wxPoint clientorigin = GetClientAreaOrigin() ; + wxSize clientsize = GetClientSize() ; + wxCursor cursor ; + if ( wxRect2DInt( clientorigin.x , clientorigin.y , clientsize.x , clientsize.y ).Contains( wxPoint2DInt( pt ) ) ) + { + wxSetCursorEvent event( pt.x , pt.y ); + + bool processedEvtSetCursor = HandleWindowEvent(event); + if ( processedEvtSetCursor && event.HasCursor() ) + { + cursor = event.GetCursor() ; + } + else + { + // the test for processedEvtSetCursor is here to prevent using m_cursor + // if the user code caught EVT_SET_CURSOR() and returned nothing from + // it - this is a way to say that our cursor shouldn't be used for this + // point + if ( !processedEvtSetCursor && m_cursor.Ok() ) + cursor = m_cursor ; + + if ( !wxIsBusy() && !GetParent() ) + cursor = *wxSTANDARD_CURSOR ; + } + + if ( cursor.Ok() ) + cursor.MacInstall() ; + } + + return cursor.Ok() ; +} + +wxString wxWindowMac::MacGetToolTipString( wxPoint &WXUNUSED(pt) ) +{ +#if wxUSE_TOOLTIPS + if ( m_tooltip ) + return m_tooltip->GetTip() ; +#endif + + return wxEmptyString ; +} + +void wxWindowMac::ClearBackground() +{ + Refresh() ; + Update() ; +} + +void wxWindowMac::Update() +{ + wxNonOwnedWindow* top = MacGetTopLevelWindow(); + if (top) + top->MacPerformUpdates() ; +} + +wxNonOwnedWindow* wxWindowMac::MacGetTopLevelWindow() const +{ + wxNonOwnedWindow* win = NULL ; + WindowRef window = (WindowRef) MacGetTopLevelWindowRef() ; + if ( window ) + win = wxFindWinFromMacWindow( window ) ; + + return win ; +} + +const wxRect& wxWindowMac::MacGetClippedClientRect() const +{ + MacUpdateClippedRects() ; + + return m_cachedClippedClientRect ; +} + +const wxRect& wxWindowMac::MacGetClippedRect() const +{ + MacUpdateClippedRects() ; + + return m_cachedClippedRect ; +} + +const wxRect&wxWindowMac:: MacGetClippedRectWithOuterStructure() const +{ + MacUpdateClippedRects() ; + + return m_cachedClippedRectWithOuterStructure ; +} + +const wxRegion& wxWindowMac::MacGetVisibleRegion( bool includeOuterStructures ) +{ + static wxRegion emptyrgn ; + + if ( !m_isBeingDeleted && IsShownOnScreen() ) + { + MacUpdateClippedRects() ; + if ( includeOuterStructures ) + return m_cachedClippedRegionWithOuterStructure ; + else + return m_cachedClippedRegion ; + } + else + { + return emptyrgn ; + } +} + +void wxWindowMac::MacUpdateClippedRects() const +{ + if ( m_cachedClippedRectValid ) + return ; + + // includeOuterStructures is true if we try to draw somthing like a focus ring etc. + // also a window dc uses this, in this case we only clip in the hierarchy for hard + // borders like a scrollwindow, splitter etc otherwise we end up in a paranoia having + // to add focus borders everywhere + + Rect r, rIncludingOuterStructures ; + + m_peer->GetRect( &r ) ; + r.left -= MacGetLeftBorderSize() ; + r.top -= MacGetTopBorderSize() ; + r.bottom += MacGetBottomBorderSize() ; + r.right += MacGetRightBorderSize() ; + + r.right -= r.left ; + r.bottom -= r.top ; + r.left = 0 ; + r.top = 0 ; + + rIncludingOuterStructures = r ; + InsetRect( &rIncludingOuterStructures , -4 , -4 ) ; + + wxRect cl = GetClientRect() ; + Rect rClient = { cl.y , cl.x , cl.y + cl.height , cl.x + cl.width } ; + + int x , y ; + wxSize size ; + const wxWindow* child = this ; + const wxWindow* parent = NULL ; + + while ( !child->IsTopLevel() && ( parent = child->GetParent() ) != NULL ) + { + if ( parent->MacIsChildOfClientArea(child) ) + { + size = parent->GetClientSize() ; + wxPoint origin = parent->GetClientAreaOrigin() ; + x = origin.x ; + y = origin.y ; + } + else + { + // this will be true for scrollbars, toolbars etc. + size = parent->GetSize() ; + y = parent->MacGetTopBorderSize() ; + x = parent->MacGetLeftBorderSize() ; + size.x -= parent->MacGetLeftBorderSize() + parent->MacGetRightBorderSize() ; + size.y -= parent->MacGetTopBorderSize() + parent->MacGetBottomBorderSize() ; + } + + parent->MacWindowToRootWindow( &x, &y ) ; + MacRootWindowToWindow( &x , &y ) ; + + Rect rparent = { y , x , y + size.y , x + size.x } ; + + // the wxwindow and client rects will always be clipped + SectRect( &r , &rparent , &r ) ; + SectRect( &rClient , &rparent , &rClient ) ; + + // the structure only at 'hard' borders + if ( parent->MacClipChildren() || + ( parent->GetParent() && parent->GetParent()->MacClipGrandChildren() ) ) + { + SectRect( &rIncludingOuterStructures , &rparent , &rIncludingOuterStructures ) ; + } + + child = parent ; + } + + m_cachedClippedRect = wxRect( r.left , r.top , r.right - r.left , r.bottom - r.top ) ; + m_cachedClippedClientRect = wxRect( rClient.left , rClient.top , + rClient.right - rClient.left , rClient.bottom - rClient.top ) ; + m_cachedClippedRectWithOuterStructure = wxRect( + rIncludingOuterStructures.left , rIncludingOuterStructures.top , + rIncludingOuterStructures.right - rIncludingOuterStructures.left , + rIncludingOuterStructures.bottom - rIncludingOuterStructures.top ) ; + + m_cachedClippedRegionWithOuterStructure = wxRegion( m_cachedClippedRectWithOuterStructure ) ; + m_cachedClippedRegion = wxRegion( m_cachedClippedRect ) ; + m_cachedClippedClientRegion = wxRegion( m_cachedClippedClientRect ) ; + + m_cachedClippedRectValid = true ; +} + +/* + This function must not change the updatergn ! + */ +bool wxWindowMac::MacDoRedraw( void* updatergnr , long time ) +{ + bool handled = false ; + Rect updatebounds ; + RgnHandle updatergn = (RgnHandle) updatergnr ; + GetRegionBounds( updatergn , &updatebounds ) ; + + // wxLogDebug(wxT("update for %s bounds %d, %d, %d, %d"), wxString(GetClassInfo()->GetClassName()).c_str(), updatebounds.left, updatebounds.top , updatebounds.right , updatebounds.bottom ) ; + + if ( !EmptyRgn(updatergn) ) + { + RgnHandle newupdate = NewRgn() ; + wxSize point = GetClientSize() ; + wxPoint origin = GetClientAreaOrigin() ; + SetRectRgn( newupdate , origin.x , origin.y , origin.x + point.x , origin.y + point.y ) ; + SectRgn( newupdate , updatergn , newupdate ) ; + + // first send an erase event to the entire update area + { + // for the toplevel window this really is the entire area + // for all the others only their client area, otherwise they + // might be drawing with full alpha and eg put blue into + // the grow-box area of a scrolled window (scroll sample) + wxDC* dc = new wxWindowDC(this); + if ( IsTopLevel() ) + dc->SetClippingRegion(wxRegion(HIShapeCreateWithQDRgn(updatergn))); + else + dc->SetClippingRegion(wxRegion(HIShapeCreateWithQDRgn(newupdate))); + + wxEraseEvent eevent( GetId(), dc ); + eevent.SetEventObject( this ); + HandleWindowEvent( eevent ); + delete dc ; + } + + MacPaintGrowBox(); + + // calculate a client-origin version of the update rgn and set m_updateRegion to that + OffsetRgn( newupdate , -origin.x , -origin.y ) ; + m_updateRegion = wxRegion(HIShapeCreateWithQDRgn(newupdate)) ; + DisposeRgn( newupdate ) ; + + if ( !m_updateRegion.Empty() ) + { + // paint the window itself + + wxPaintEvent event; + event.SetTimestamp(time); + event.SetEventObject(this); + HandleWindowEvent(event); + handled = true ; + } + + // now we cannot rely on having its borders drawn by a window itself, as it does not + // get the updateRgn wide enough to always do so, so we do it from the parent + // this would also be the place to draw any custom backgrounds for native controls + // in Composited windowing + wxPoint clientOrigin = GetClientAreaOrigin() ; + + wxWindowMac *child; + int x, y, w, h; + for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); node; node = node->GetNext()) + { + child = node->GetData(); + if (child == NULL) + continue; + if (child == m_vScrollBar) + continue; + if (child == m_hScrollBar) + continue; + if (child->IsTopLevel()) + continue; + if (!child->IsShown()) + continue; + + // only draw those in the update region (add a safety margin of 10 pixels for shadow effects + + child->GetPosition( &x, &y ); + child->GetSize( &w, &h ); + Rect childRect = { y , x , y + h , x + w } ; + OffsetRect( &childRect , clientOrigin.x , clientOrigin.y ) ; + InsetRect( &childRect , -10 , -10) ; + + if ( RectInRgn( &childRect , updatergn ) ) + { + // paint custom borders + wxNcPaintEvent eventNc( child->GetId() ); + eventNc.SetEventObject( child ); + if ( !child->HandleWindowEvent( eventNc ) ) + { + child->MacPaintBorders(0, 0) ; + } + } + } + } + + return handled ; +} + + +WXWindow wxWindowMac::MacGetTopLevelWindowRef() const +{ + wxWindowMac *iter = (wxWindowMac*)this ; + + while ( iter ) + { + if ( iter->IsTopLevel() ) + { + wxTopLevelWindow* toplevel = wxDynamicCast(iter,wxTopLevelWindow); + if ( toplevel ) + return toplevel->MacGetWindowRef(); +#if wxUSE_POPUPWIN + wxPopupWindow* popupwin = wxDynamicCast(iter,wxPopupWindow); + if ( popupwin ) + return popupwin->MacGetWindowRef(); +#endif + } + iter = iter->GetParent() ; + } + + return NULL ; +} + +bool wxWindowMac::MacHasScrollBarCorner() const +{ + /* Returns whether the scroll bars in a wxScrolledWindow should be + * shortened. Scroll bars should be shortened if either: + * + * - both scroll bars are visible, or + * + * - there is a resize box in the parent frame's corner and this + * window shares the bottom and right edge with the parent + * frame. + */ + + if ( m_hScrollBar == NULL && m_vScrollBar == NULL ) + return false; + + if ( ( m_hScrollBar && m_hScrollBar->IsShown() ) + && ( m_vScrollBar && m_vScrollBar->IsShown() ) ) + { + // Both scroll bars visible + return true; + } + else + { + wxPoint thisWindowBottomRight = GetScreenRect().GetBottomRight(); + + for ( const wxWindow *win = this; win; win = win->GetParent() ) + { + const wxFrame *frame = wxDynamicCast( win, wxFrame ) ; + if ( frame ) + { + if ( frame->GetWindowStyleFlag() & wxRESIZE_BORDER ) + { + // Parent frame has resize handle + wxPoint frameBottomRight = frame->GetScreenRect().GetBottomRight(); + + // Note: allow for some wiggle room here as wxMac's + // window rect calculations seem to be imprecise + if ( abs( thisWindowBottomRight.x - frameBottomRight.x ) <= 2 + && abs( thisWindowBottomRight.y - frameBottomRight.y ) <= 2 ) + { + // Parent frame has resize handle and shares + // right bottom corner + return true ; + } + else + { + // Parent frame has resize handle but doesn't + // share right bottom corner + return false ; + } + } + else + { + // Parent frame doesn't have resize handle + return false ; + } + } + } + + // No parent frame found + return false ; + } +} + +void wxWindowMac::MacCreateScrollBars( long style ) +{ + wxASSERT_MSG( m_vScrollBar == NULL && m_hScrollBar == NULL , wxT("attempt to create window twice") ) ; + + if ( style & ( wxVSCROLL | wxHSCROLL ) ) + { + int scrlsize = MAC_SCROLLBAR_SIZE ; + if ( GetWindowVariant() == wxWINDOW_VARIANT_SMALL || GetWindowVariant() == wxWINDOW_VARIANT_MINI ) + { + scrlsize = MAC_SMALL_SCROLLBAR_SIZE ; + } + + int adjust = MacHasScrollBarCorner() ? scrlsize - 1: 0 ; + int width, height ; + GetClientSize( &width , &height ) ; + + wxPoint vPoint(width - scrlsize, 0) ; + wxSize vSize(scrlsize, height - adjust) ; + wxPoint hPoint(0, height - scrlsize) ; + wxSize hSize(width - adjust, scrlsize) ; + + // we have to set the min size to a smaller value, otherwise they cannot get smaller (InitialSize sets MinSize) + if ( style & wxVSCROLL ) + { + m_vScrollBar = new wxScrollBar((wxWindow*)this, wxID_ANY, vPoint, vSize , wxVERTICAL); + m_vScrollBar->SetMinSize( wxDefaultSize ); + } + + if ( style & wxHSCROLL ) + { + m_hScrollBar = new wxScrollBar((wxWindow*)this, wxID_ANY, hPoint, hSize , wxHORIZONTAL); + m_hScrollBar->SetMinSize( wxDefaultSize ); + } + } + + // because the create does not take into account the client area origin + // we might have a real position shift + MacRepositionScrollBars() ; +} + +bool wxWindowMac::MacIsChildOfClientArea( const wxWindow* child ) const +{ + bool result = ((child == NULL) || ((child != m_hScrollBar) && (child != m_vScrollBar))); + + return result ; +} + +void wxWindowMac::MacRepositionScrollBars() +{ + if ( !m_hScrollBar && !m_vScrollBar ) + return ; + + int scrlsize = m_hScrollBar ? m_hScrollBar->GetSize().y : ( m_vScrollBar ? m_vScrollBar->GetSize().x : MAC_SCROLLBAR_SIZE ) ; + int adjust = MacHasScrollBarCorner() ? scrlsize - 1 : 0 ; + + // get real client area + int width, height ; + GetSize( &width , &height ); + + width -= MacGetLeftBorderSize() + MacGetRightBorderSize(); + height -= MacGetTopBorderSize() + MacGetBottomBorderSize(); + + wxPoint vPoint( width - scrlsize, 0 ) ; + wxSize vSize( scrlsize, height - adjust ) ; + wxPoint hPoint( 0 , height - scrlsize ) ; + wxSize hSize( width - adjust, scrlsize ) ; + +#if 0 + int x = 0, y = 0, w, h ; + GetSize( &w , &h ) ; + + MacClientToRootWindow( &x , &y ) ; + MacClientToRootWindow( &w , &h ) ; + + wxWindowMac *iter = (wxWindowMac*)this ; + + int totW = 10000 , totH = 10000; + while ( iter ) + { + if ( iter->IsTopLevel() ) + { + iter->GetSize( &totW , &totH ) ; + break ; + } + + iter = iter->GetParent() ; + } + + if ( x == 0 ) + { + hPoint.x = -1 ; + hSize.x += 1 ; + } + if ( y == 0 ) + { + vPoint.y = -1 ; + vSize.y += 1 ; + } + + if ( w - x >= totW ) + { + hSize.x += 1 ; + vPoint.x += 1 ; + } + if ( h - y >= totH ) + { + vSize.y += 1 ; + hPoint.y += 1 ; + } +#endif + + if ( m_vScrollBar ) + m_vScrollBar->SetSize( vPoint.x , vPoint.y, vSize.x, vSize.y , wxSIZE_ALLOW_MINUS_ONE ); + if ( m_hScrollBar ) + m_hScrollBar->SetSize( hPoint.x , hPoint.y, hSize.x, hSize.y, wxSIZE_ALLOW_MINUS_ONE ); +} + +bool wxWindowMac::AcceptsFocus() const +{ + return MacCanFocus() && wxWindowBase::AcceptsFocus(); +} + +void wxWindowMac::MacSuperChangedPosition() +{ + // only window-absolute structures have to be moved i.e. controls + + m_cachedClippedRectValid = false ; + + wxWindowMac *child; + wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + while ( node ) + { + child = node->GetData(); + child->MacSuperChangedPosition() ; + + node = node->GetNext(); + } +} + +void wxWindowMac::MacTopLevelWindowChangedPosition() +{ + // only screen-absolute structures have to be moved i.e. glcanvas + + wxWindowMac *child; + wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); + while ( node ) + { + child = node->GetData(); + child->MacTopLevelWindowChangedPosition() ; + + node = node->GetNext(); + } +} + +long wxWindowMac::MacGetLeftBorderSize() const +{ + if ( IsTopLevel() ) + return 0 ; + + SInt32 border = 0 ; + + if (HasFlag(wxRAISED_BORDER) || HasFlag( wxSUNKEN_BORDER) || HasFlag(wxDOUBLE_BORDER)) + { + // this metric is only the 'outset' outside the simple frame rect + GetThemeMetric( kThemeMetricEditTextFrameOutset , &border ) ; + border += 1 ; + } + else if (HasFlag(wxSIMPLE_BORDER)) + { + // this metric is only the 'outset' outside the simple frame rect + GetThemeMetric( kThemeMetricListBoxFrameOutset , &border ) ; + border += 1 ; + } + + return border ; +} + +long wxWindowMac::MacGetRightBorderSize() const +{ + // they are all symmetric in mac themes + return MacGetLeftBorderSize() ; +} + +long wxWindowMac::MacGetTopBorderSize() const +{ + // they are all symmetric in mac themes + return MacGetLeftBorderSize() ; +} + +long wxWindowMac::MacGetBottomBorderSize() const +{ + // they are all symmetric in mac themes + return MacGetLeftBorderSize() ; +} + +long wxWindowMac::MacRemoveBordersFromStyle( long style ) +{ + return style & ~wxBORDER_MASK ; +} + +// Find the wxWindowMac at the current mouse position, returning the mouse +// position. +wxWindowMac * wxFindWindowAtPointer( wxPoint& pt ) +{ + pt = wxGetMousePosition(); + wxWindowMac* found = wxFindWindowAtPoint(pt); + + return found; +} + +// Get the current mouse position. +wxPoint wxGetMousePosition() +{ + int x, y; + + wxGetMousePosition( &x, &y ); + + return wxPoint(x, y); +} + +void wxWindowMac::OnMouseEvent( wxMouseEvent &event ) +{ + if ( event.GetEventType() == wxEVT_RIGHT_DOWN ) + { + // copied from wxGTK : CS + // VZ: shouldn't we move this to base class then? + + // generate a "context menu" event: this is similar to wxEVT_RIGHT_DOWN + // except that: + // + // (a) it's a command event and so is propagated to the parent + // (b) under MSW it can be generated from kbd too + // (c) it uses screen coords (because of (a)) + wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, + this->GetId(), + this->ClientToScreen(event.GetPosition())); + evtCtx.SetEventObject(this); + if ( ! HandleWindowEvent(evtCtx) ) + event.Skip() ; + } + else + { + event.Skip() ; + } +} + +void wxWindowMac::OnPaint( wxPaintEvent & WXUNUSED(event) ) +{ + // for native controls: call their native paint method + if ( !MacIsUserPane() || ( IsTopLevel() && GetBackgroundStyle() == wxBG_STYLE_SYSTEM ) ) + { + if ( wxTheApp->MacGetCurrentEvent() != NULL && wxTheApp->MacGetCurrentEventHandlerCallRef() != NULL + && GetBackgroundStyle() != wxBG_STYLE_TRANSPARENT ) + CallNextEventHandler( + (EventHandlerCallRef)wxTheApp->MacGetCurrentEventHandlerCallRef() , + (EventRef) wxTheApp->MacGetCurrentEvent() ) ; + } +} + +void wxWindowMac::MacHandleControlClick(WXWidget WXUNUSED(control), + wxInt16 WXUNUSED(controlpart), + bool WXUNUSED(mouseStillDown)) +{ +} + +Rect wxMacGetBoundsForControl( wxWindow* window , const wxPoint& pos , const wxSize &size , bool adjustForOrigin ) +{ + int x, y, w, h ; + + window->MacGetBoundsForControl( pos , size , x , y, w, h , adjustForOrigin ) ; + Rect bounds = { y, x, y + h, x + w }; + + return bounds ; +} + +wxInt32 wxWindowMac::MacControlHit(WXEVENTHANDLERREF WXUNUSED(handler) , WXEVENTREF WXUNUSED(event) ) +{ + return eventNotHandledErr ; +} + +bool wxWindowMac::Reparent(wxWindowBase *newParentBase) +{ + wxWindowMac *newParent = (wxWindowMac *)newParentBase; + if ( !wxWindowBase::Reparent(newParent) ) + return false; + + // copied from MacPostControlCreate + ControlRef container = (ControlRef) GetParent()->GetHandle() ; + + wxASSERT_MSG( container != NULL , wxT("No valid mac container control") ) ; + + ::EmbedControl( m_peer->GetControlRef() , container ) ; + + return true; +} + +bool wxWindowMac::SetTransparent(wxByte alpha) +{ + SetBackgroundStyle(wxBG_STYLE_TRANSPARENT); + + if ( alpha != m_macAlpha ) + { + m_macAlpha = alpha ; + Refresh() ; + } + return true ; +} + + +bool wxWindowMac::CanSetTransparent() +{ + return true ; +} + +wxByte wxWindowMac::GetTransparent() const +{ + return m_macAlpha ; +} + +bool wxWindowMac::IsShownOnScreen() const +{ +#if TARGET_API_MAC_OSX + if ( m_peer && m_peer->Ok() ) + { + bool peerVis = m_peer->IsVisible(); + bool wxVis = wxWindowBase::IsShownOnScreen(); + if( peerVis != wxVis ) + { + // CS : put a breakpoint here to investigate differences + // between native an wx visibilities + // the only place where I've encountered them until now + // are the hiding/showing sequences where the vis-changed event is + // first sent to the innermost control, while wx does things + // from the outmost control + wxVis = wxWindowBase::IsShownOnScreen(); + return wxVis; + } + + return m_peer->IsVisible(); + } +#endif + + return wxWindowBase::IsShownOnScreen(); +} diff --git a/src/osx/carbon/wxmac.icns b/src/osx/carbon/wxmac.icns new file mode 100644 index 0000000000000000000000000000000000000000..2c4e6f097d2f4315aaabf16668dbab4f4db6ebd0 GIT binary patch literal 36901 zcmeHP4|r46**`Z)n>3`5u*sgW@O(w8TWlLmVY0Lp($q1met_8!9{gZ)VK$|r+bZo? zwcMNju^@Hm#BQG-6CYZg@>Oh0DB$W+bha`?Y!#5w0G4^>a~Suy5(XRhd*5^KP17`m z^bd2-yt%o5&UxSW_x{fBoZQ@=6DV}wUrflR>u$?iFoO_AM=tl8`zay$TM1eGV?wMi z6Y}jtgxvi@Lay)Jbj@3=E-mecDcAI}E!y~I*ecPXvp)=?vS03x#usRqtJcdEJ9S5*WvOIqqa_5 zw{G3q`v<}&9xvbvo*F18KU7^(^6)@;qG9dox(NT|hYIRS$|LOlr%LKRIRG!QF8M$y zzs7_wKOwJmb$`;aq_D)uw41TK@Ab^vA1Wy=^qWnL8O!^!uUk^bulAeTP3=&AUA;4_ zFAz9lG@B*=YcpGJBFP5E)QU@{85uKQCdo!) ziirXLu6=neXn&=>t7jVK%S&^1oh0E`@_~2Fcfk1ioL(>vzD0=Ua~TJpOOqQq-*!P<<44JvtS`*cTkok{&_YF5kfQ_f9uILv>i>^eFc(uo8w#fQvVhhvJPre>W4ESRH^rhA%)+MvOG}%#Lx&QLK1sGEBqV8dInJur$VcmiB-iHcI z8Zcbs1kJ^efKhrH#h)r$TDFvPGp^H^$WQD0`%d^sr>5+@`yp9u*9gfRm#J$->w zd?Qr7N@FOwV1F-rO5J;^?{}P_YUV?jN1*I9G!GYG_#I$sI1{Xt^L!i%oaj5%+t=H| z2@`(*Vq2SUPum{fo~Cz@1P@;-xc4+YA&@|IFL^@{J`t$g%l@+}pL_@8E~;;K7Zl#> zDJY<&J^Sx*7kFT!rQKck6nIwMTjZgo*8TSu-CI=PxtEr9b=|WH;_m**Jym{``%6Ww z``u`F9pOI~w*WM2a?2vuT`t$6*}D(A?_E{karFkzczM;I0wABNDR&EJ*Wc#z1F(CQ z>qM}UXE4arlz+^A%beM>A9$dkXjKtfI9~IoK3Y~LfUie_H26=Xt?XGi&8EO%Fe;fo(YDR4u>!m zFA=1v=Gf=hr=jpYl%SaDZ9u}XbP)cPhNh;*9ZfqLcRUNitD0<1AUFroWr6TVt2dFy z!@~dgk*Z3v281tu`{<6Qoy|L&nrP|h>E_0!R@i9i{g1%5tJ$}cmX4in_BH#OTAO9t zE{J=!_`6llkTW2B@v+m5Xy0hJJ6%p!zBAw6a2-1d*p0h3{xtY~uTw=Mr#3m?y)GlelKtwQ);+uTg6*^K zJLfs)&zUDiI=fhl<|bRVEt@M%EH3`^`%b5ebuxCUi}PJeMtX)NJ^epGmnMvy53w{- z-{kC?o?*3E(rq?QQ2QWKPKl=UG~6uEa}%11X~tXgC9X7Mh9$#t4a7D6kk7A>)6My# zB|QTJr(-wd&C8#6NB$l2<}Y{`n`p5C(F}{lYNOpyOEzIQ-1n{3MPI1L5BS8(ubAH~ zvJuXow>?ZHl1R8R;)2>E%F&nqY&+(QEp`wl+o^IS-<{z}Nl83>*VgqU zsWIaNl459iwaK_ucYn(doJ3Q%dQ(D5@`H!SCPKE~l$x^DaQM(#J0VEqyVN~BIjPjdfrf8UQ%ANHd=xA z3RK`pcNN z%P=E##)qX(mEK=^kKY8DjX0}B$Oi^zi}R%y?>^gm_IG{;&?k#H?;2VlAoT`tX(#Om zOzlRIPqQKO_H3s!3+j6efMYg^e7X&{WM;NBFF=3e0VB>ziF^#H(1KZTmn3fyQ+qqi zPKo^eGn<{a%&?-r-v~f-mWq&W!wi^sYR-rHcMJ@8%l?L0&6!Th=PmSx1Qh3k$A?8v8`qAph~46DFF8&m{v>DDS{{p z46i`LL3t`tJP+?FTDZ=|Tg2%(oPz9p@k;&;TL?}@()mC*zIi2oZW~z$GlLzS^$1^o z@Jjv{I+z;{c5Fj@Jj9;Ri~s>X=KvnTaf&n!J`7B@igX|-4*bM(2u;~{bjF^bjo`y> z6Y-Hur}rW6Qt#3g53LZ7vDQx-wj`zjJhT-yJ_zvEx2-BdXDiViCebd0c`U~{Pn`HPH*e`*0NPXfomM6hu&J*;NfU{0*3L9%VUTwl z31Mro&t3$4m=DDXUu31fD~!*X`?a}_+$k`(3p2~Kf02})=~q#BoW|9EJ%Udb(NRjd zH66$WU!Cm-tuTKFQ_gh88PY_w*Gxj@X<0D&T61wdyf1KN7q|_XK;JOhMh-!puIEz@c$OkUV`{p zkN}Qhe5@Tod@jMNACBNNA0AV8cwhUsK0gZ#w+>md9opeIf=1OAlU2z<~-bcb_< zKFb}Hk3%Fhz+cr5G%x@=Y!Lr`EtLl-O$+n4S*_@-#$yJAMDU-II1y*&K_HY>(kJ9y z?k)3P@GjxJ7nX7yH_%_pfi+5>n}vNK^AF26_gCi5`;p^Exho$V;-FM0 z3Jjw_p$C^0bhG8zjbFmzLh=S!T-YK^av3)*)DhV9L=Q(=l_u=g=?MHyO?O&#ov>Yp zcBs@7W7ta!!%E4E20|Q$N?s61}lqI{Sqc<8L2d!tT1x?v!JRkJ(lUAgKAWE-?2J4O8Lg*1e6~w@=lm$aU znEem%ylZ;!H6Ls9qKEA-fi{BeXZ5;sbl7&}u;)q}Hiw4 zg!HVKK0)MPiSbhEU_|OrfXV}jp?x$p&kFjWAZ@5XVi_L_Q3`6v0~n0I_|Y=bP%Ws+ zcn}VNc{ZSe_$>k{dvq^ZArH!DE z^&AdQGt};PQ!ICps{;n#z&ij+JW@32wqjt4G$DnnBN(iKg%=nH8uvHt_FT?xvZa=X z0K(%b0*0=RP%v1{s{$CTcIWykE9{8ir~Ci}9S{M`Uv=k)+FWWe*FTAoAxh-kyWmRl7PM zRF5BWx?vM^?)<$SQjIHs0v8!$K9l@nEmVL2Goig+4;|$YJA+ z{}WvH2&^nYZ)n^Wo9$-KX0oLIAp{t$tv=we@kU>$jCB$h=Ty7w2%&c0+*szdm}~%a zlJW#Ba0`}IJ5@BvG`BO&l$IKk!SWaqv$7T(Oqxcqd1}-L7xVy2OWA`9XCYP=kTgEbCO0k#&6vev^IFK% z3PB4PG?CY-w!7q3HPTik&|DHmsLbn3#Fjfn8ELE3z!{n^k$IALNxgwB@?jFONjAVR z`fh|zffI6wJEG{^_P|z3hP43HbUL;dY;ZB|MjYD~dxeDnyb83VV|!}k*!Iw6L1p@6 zn5QbGnkpdQ3+3~bWRd;kv*JHk0a+LMJlHz4B>TlY4|N~3COaf1>_5a3yF&4obPDE?Hdan>gcH{>F^XNEW^6^a7GC{XAw#s$CmqFKYL9r}}MM@Hpcn+y(@CfztV zY|=@(U0MfQx%iSC00)-E$z_(slRJ+4(fRnA5_<073+S@ofwkrB?f&HM-xb^kKZjTC zyY0PNhVdJ^QxCzSqEWLi+i!sSAi z5&HaMoL3*1)(kDgdGmoK>k$4u^U>F3e`@0=F57%hyis<0TR z#h53X1mH{;nF3rcYpZ7$rFHc zEYQ>2b5an3+$ZEOy9D5H_&KhDyaSKrzSfnjhin55zSjy03ku~cPk9^ro&hk*e=sla z_XP!ozxp%Fo8N=-hMd{16{}Z24O0*wz60g;Ia%4i7KGP7VR-|!!>=-*6oh?eS^i64 z|JglRIXPfIdi2QABgcUY*GW!O4#!{P_K{x5yyyLwceb?b#Vh#V&N8|U{;AsV zW!W$ObC}J@E(+bN@ty;pQ$iAm>f{86%bziMnd2KeO&BQZK11sv4(~CZuv7S=EjBhSE%NYHd*E9VFrrkf`+S^kMW&<%83>Y-z1$mHL z=MY1TigR$sSX?ar`vNqdhkqN<*||9E3IEmt*8Udh7)HKFzzSKJ4wj$9dxYafdXIpA zWPtaGOjxnPdx8qqVZd&m;&5caJ>y*Z^|~~M5V#>|wFCF3aFSsz3LnIKz*iqdvou~t zeEL6uC2aCDgf(^zb5*Nn$p+XNyas{hr1cBY{g($cra1g_#l9(!fpDGAPEZoB^ zq#UEQQC<~DPC|$zHF~2=bD-&wxbazOc3>4Nax^v?gIy3yzX777b%+#ed-V<+MPk(BgQsp-6{ch~_Ay>~sy||C~dompGSomtB7w}b^&n|o>pZyMe z)yBN4^{_=7U}roY(do(+tcQWSQuN=dzgikV>C9n@pVyN z6-Z7(kV%c+C^uQADmGoly}T8(4vGU-a^zTT;Vy{QPX->FL!^@1Irys0hni>=2N?!6 zl;s+DHZp9)R`@zip-E9Dp#qE>} z--788kBJrz8fSe_q6+cE)C7^EVGGjYuUUpRa_mMsf z4ET@ffufRK|4b#j1|BG4Ue&g;z74Q5t*xTdl`Gg*{6_i63sG?CgM%0@mBdu>`&2|mYjq<8MauPx$snHu{ngdOb#Es8NvjeMGk)yHE80>;r`V9~rtwW?(+vmaq zMJ3c=_YZ53We|zY^}>&fV29tTu=pDjmka(Ww+pV=NU*CK1J4^%DQ$QF`3T2D6&XVA}Pm}C`5Q6&+@fg)@2FA}~vO4ChJ_YI*L zmm}@7EtTQMZ-BWeEydBb;68!*1H+M81RnTjj z)FD^TL%p~UJd%y{>M`L#y7cE5)w1+3 zt2;`yqJW}+qJW}+qJW}+qJW}+qJW}+qJW}+qQKY`7{Q;nV@otlw30t=1%^>@Y}IjR zALf4$9b5Jh{Z@6Xsba<}?T=f5nEH*iGVbik_#3O#G2<2e<5nQ1eq*hSJG(Oe$0~Kq zct!uX6^N + +void wxMacConvertNewlines13To10( char * data ) +{ + char * buf = data ; + while( (buf=strchr(buf,0x0d)) != NULL ) + { + *buf = 0x0a ; + buf++ ; + } +} + +void wxMacConvertNewlines10To13( char * data ) +{ + char * buf = data ; + while( (buf=strchr(buf,0x0a)) != NULL ) + { + *buf = 0x0d ; + buf++ ; + } +} + +const wxString sCR((wxChar)13); +const wxString sLF((wxChar)10); + +void wxMacConvertNewlines13To10( wxString * data ) +{ + data->Replace( sCR,sLF); +} + +void wxMacConvertNewlines10To13( wxString * data ) +{ + data->Replace( sLF,sCR); +} + +wxUint32 wxMacGetSystemEncFromFontEnc(wxFontEncoding encoding) +{ + CFStringEncoding enc = 0 ; + if ( encoding == wxFONTENCODING_DEFAULT ) + { +#if wxUSE_GUI + encoding = wxFont::GetDefaultEncoding() ; +#else + encoding = wxFONTENCODING_SYSTEM; // to be set below +#endif + } + + if ( encoding == wxFONTENCODING_SYSTEM ) + { + enc = CFStringGetSystemEncoding(); + } + + switch( encoding) + { + case wxFONTENCODING_ISO8859_1 : + enc = kCFStringEncodingISOLatin1 ; + break ; + case wxFONTENCODING_ISO8859_2 : + enc = kCFStringEncodingISOLatin2; + break ; + case wxFONTENCODING_ISO8859_3 : + enc = kCFStringEncodingISOLatin3 ; + break ; + case wxFONTENCODING_ISO8859_4 : + enc = kCFStringEncodingISOLatin4; + break ; + case wxFONTENCODING_ISO8859_5 : + enc = kCFStringEncodingISOLatinCyrillic; + break ; + case wxFONTENCODING_ISO8859_6 : + enc = kCFStringEncodingISOLatinArabic; + break ; + case wxFONTENCODING_ISO8859_7 : + enc = kCFStringEncodingISOLatinGreek; + break ; + case wxFONTENCODING_ISO8859_8 : + enc = kCFStringEncodingISOLatinHebrew; + break ; + case wxFONTENCODING_ISO8859_9 : + enc = kCFStringEncodingISOLatin5; + break ; + case wxFONTENCODING_ISO8859_10 : + enc = kCFStringEncodingISOLatin6; + break ; + case wxFONTENCODING_ISO8859_13 : + enc = kCFStringEncodingISOLatin7; + break ; + case wxFONTENCODING_ISO8859_14 : + enc = kCFStringEncodingISOLatin8; + break ; + case wxFONTENCODING_ISO8859_15 : + enc = kCFStringEncodingISOLatin9; + break ; + + case wxFONTENCODING_KOI8 : + enc = kCFStringEncodingKOI8_R; + break ; + case wxFONTENCODING_ALTERNATIVE : // MS-DOS CP866 + enc = kCFStringEncodingDOSRussian; + break ; +/* + case wxFONTENCODING_BULGARIAN : + enc = ; + break ; +*/ + case wxFONTENCODING_CP437 : + enc =kCFStringEncodingDOSLatinUS ; + break ; + case wxFONTENCODING_CP850 : + enc = kCFStringEncodingDOSLatin1; + break ; + case wxFONTENCODING_CP852 : + enc = kCFStringEncodingDOSLatin2; + break ; + case wxFONTENCODING_CP855 : + enc = kCFStringEncodingDOSCyrillic; + break ; + case wxFONTENCODING_CP866 : + enc =kCFStringEncodingDOSRussian ; + break ; + case wxFONTENCODING_CP874 : + enc = kCFStringEncodingDOSThai; + break ; + case wxFONTENCODING_CP932 : + enc = kCFStringEncodingDOSJapanese; + break ; + case wxFONTENCODING_CP936 : + enc = kCFStringEncodingDOSChineseSimplif ; + break ; + case wxFONTENCODING_CP949 : + enc = kCFStringEncodingDOSKorean; + break ; + case wxFONTENCODING_CP950 : + enc = kCFStringEncodingDOSChineseTrad; + break ; + + case wxFONTENCODING_CP1250 : + enc = kCFStringEncodingWindowsLatin2; + break ; + case wxFONTENCODING_CP1251 : + enc =kCFStringEncodingWindowsCyrillic ; + break ; + case wxFONTENCODING_CP1252 : + enc =kCFStringEncodingWindowsLatin1 ; + break ; + case wxFONTENCODING_CP1253 : + enc = kCFStringEncodingWindowsGreek; + break ; + case wxFONTENCODING_CP1254 : + enc = kCFStringEncodingWindowsLatin5; + break ; + case wxFONTENCODING_CP1255 : + enc =kCFStringEncodingWindowsHebrew ; + break ; + case wxFONTENCODING_CP1256 : + enc =kCFStringEncodingWindowsArabic ; + break ; + case wxFONTENCODING_CP1257 : + enc = kCFStringEncodingWindowsBalticRim; + break ; +#if 0 + case wxFONTENCODING_UTF7 : + enc = CreateTextEncoding(kCFStringEncodingUnicodeDefault,0,kUnicodeUTF7Format) ; +#endif + break ; + case wxFONTENCODING_UTF8 : + enc = kCFStringEncodingUTF8; + break ; + case wxFONTENCODING_EUC_JP : + enc = kCFStringEncodingEUC_JP; + break ; + case wxFONTENCODING_UTF16BE : + enc = kCFStringEncodingUTF16BE; + break ; + case wxFONTENCODING_UTF16LE : + enc = kCFStringEncodingUTF16LE; + break ; + case wxFONTENCODING_UTF32BE : + enc = kCFStringEncodingUTF32BE; + break ; + case wxFONTENCODING_UTF32LE : + enc = kCFStringEncodingUTF32LE; + break ; + + case wxFONTENCODING_MACROMAN : + enc = kCFStringEncodingMacRoman ; + break ; + case wxFONTENCODING_MACJAPANESE : + enc = kCFStringEncodingMacJapanese ; + break ; + case wxFONTENCODING_MACCHINESETRAD : + enc = kCFStringEncodingMacChineseTrad ; + break ; + case wxFONTENCODING_MACKOREAN : + enc = kCFStringEncodingMacKorean ; + break ; + case wxFONTENCODING_MACARABIC : + enc = kCFStringEncodingMacArabic ; + break ; + case wxFONTENCODING_MACHEBREW : + enc = kCFStringEncodingMacHebrew ; + break ; + case wxFONTENCODING_MACGREEK : + enc = kCFStringEncodingMacGreek ; + break ; + case wxFONTENCODING_MACCYRILLIC : + enc = kCFStringEncodingMacCyrillic ; + break ; + case wxFONTENCODING_MACDEVANAGARI : + enc = kCFStringEncodingMacDevanagari ; + break ; + case wxFONTENCODING_MACGURMUKHI : + enc = kCFStringEncodingMacGurmukhi ; + break ; + case wxFONTENCODING_MACGUJARATI : + enc = kCFStringEncodingMacGujarati ; + break ; + case wxFONTENCODING_MACORIYA : + enc = kCFStringEncodingMacOriya ; + break ; + case wxFONTENCODING_MACBENGALI : + enc = kCFStringEncodingMacBengali ; + break ; + case wxFONTENCODING_MACTAMIL : + enc = kCFStringEncodingMacTamil ; + break ; + case wxFONTENCODING_MACTELUGU : + enc = kCFStringEncodingMacTelugu ; + break ; + case wxFONTENCODING_MACKANNADA : + enc = kCFStringEncodingMacKannada ; + break ; + case wxFONTENCODING_MACMALAJALAM : + enc = kCFStringEncodingMacMalayalam ; + break ; + case wxFONTENCODING_MACSINHALESE : + enc = kCFStringEncodingMacSinhalese ; + break ; + case wxFONTENCODING_MACBURMESE : + enc = kCFStringEncodingMacBurmese ; + break ; + case wxFONTENCODING_MACKHMER : + enc = kCFStringEncodingMacKhmer ; + break ; + case wxFONTENCODING_MACTHAI : + enc = kCFStringEncodingMacThai ; + break ; + case wxFONTENCODING_MACLAOTIAN : + enc = kCFStringEncodingMacLaotian ; + break ; + case wxFONTENCODING_MACGEORGIAN : + enc = kCFStringEncodingMacGeorgian ; + break ; + case wxFONTENCODING_MACARMENIAN : + enc = kCFStringEncodingMacArmenian ; + break ; + case wxFONTENCODING_MACCHINESESIMP : + enc = kCFStringEncodingMacChineseSimp ; + break ; + case wxFONTENCODING_MACTIBETAN : + enc = kCFStringEncodingMacTibetan ; + break ; + case wxFONTENCODING_MACMONGOLIAN : + enc = kCFStringEncodingMacMongolian ; + break ; + case wxFONTENCODING_MACETHIOPIC : + enc = kCFStringEncodingMacEthiopic ; + break ; + case wxFONTENCODING_MACCENTRALEUR : + enc = kCFStringEncodingMacCentralEurRoman ; + break ; + case wxFONTENCODING_MACVIATNAMESE : + enc = kCFStringEncodingMacVietnamese ; + break ; + case wxFONTENCODING_MACARABICEXT : + enc = kCFStringEncodingMacExtArabic ; + break ; + case wxFONTENCODING_MACSYMBOL : + enc = kCFStringEncodingMacSymbol ; + break ; + case wxFONTENCODING_MACDINGBATS : + enc = kCFStringEncodingMacDingbats ; + break ; + case wxFONTENCODING_MACTURKISH : + enc = kCFStringEncodingMacTurkish ; + break ; + case wxFONTENCODING_MACCROATIAN : + enc = kCFStringEncodingMacCroatian ; + break ; + case wxFONTENCODING_MACICELANDIC : + enc = kCFStringEncodingMacIcelandic ; + break ; + case wxFONTENCODING_MACROMANIAN : + enc = kCFStringEncodingMacRomanian ; + break ; + case wxFONTENCODING_MACCELTIC : + enc = kCFStringEncodingMacCeltic ; + break ; + case wxFONTENCODING_MACGAELIC : + enc = kCFStringEncodingMacGaelic ; + break ; + case wxFONTENCODING_MACKEYBOARD : + enc = 41; /* kTextEncodingMacKeyboardGlyphs ; */ + break ; + default : // to make gcc happy + break ; + }; + return enc ; +} + +wxFontEncoding wxMacGetFontEncFromSystemEnc(wxUint32 encoding) +{ + wxFontEncoding enc = wxFONTENCODING_DEFAULT ; + + switch( encoding) + { + case kCFStringEncodingISOLatin1 : + enc = wxFONTENCODING_ISO8859_1 ; + break ; + case kCFStringEncodingISOLatin2 : + enc = wxFONTENCODING_ISO8859_2; + break ; + case kCFStringEncodingISOLatin3 : + enc = wxFONTENCODING_ISO8859_3 ; + break ; + case kCFStringEncodingISOLatin4 : + enc = wxFONTENCODING_ISO8859_4; + break ; + case kCFStringEncodingISOLatinCyrillic : + enc = wxFONTENCODING_ISO8859_5; + break ; + case kCFStringEncodingISOLatinArabic : + enc = wxFONTENCODING_ISO8859_6; + break ; + case kCFStringEncodingISOLatinGreek : + enc = wxFONTENCODING_ISO8859_7; + break ; + case kCFStringEncodingISOLatinHebrew : + enc = wxFONTENCODING_ISO8859_8; + break ; + case kCFStringEncodingISOLatin5 : + enc = wxFONTENCODING_ISO8859_9; + break ; + case kCFStringEncodingISOLatin6 : + enc = wxFONTENCODING_ISO8859_10; + break ; + case kCFStringEncodingISOLatin7 : + enc = wxFONTENCODING_ISO8859_13; + break ; + case kCFStringEncodingISOLatin8 : + enc = wxFONTENCODING_ISO8859_14; + break ; + case kCFStringEncodingISOLatin9 : + enc =wxFONTENCODING_ISO8859_15 ; + break ; + + case kCFStringEncodingKOI8_R : + enc = wxFONTENCODING_KOI8; + break ; +/* + case : + enc = wxFONTENCODING_BULGARIAN; + break ; +*/ + case kCFStringEncodingDOSLatinUS : + enc = wxFONTENCODING_CP437; + break ; + case kCFStringEncodingDOSLatin1 : + enc = wxFONTENCODING_CP850; + break ; + case kCFStringEncodingDOSLatin2 : + enc =wxFONTENCODING_CP852 ; + break ; + case kCFStringEncodingDOSCyrillic : + enc = wxFONTENCODING_CP855; + break ; + case kCFStringEncodingDOSRussian : + enc = wxFONTENCODING_CP866; + break ; + case kCFStringEncodingDOSThai : + enc =wxFONTENCODING_CP874 ; + break ; + case kCFStringEncodingDOSJapanese : + enc = wxFONTENCODING_CP932; + break ; + case kCFStringEncodingDOSChineseSimplif : + enc = wxFONTENCODING_CP936; + break ; + case kCFStringEncodingDOSKorean : + enc = wxFONTENCODING_CP949; + break ; + case kCFStringEncodingDOSChineseTrad : + enc = wxFONTENCODING_CP950; + break ; + + case kCFStringEncodingWindowsLatin2 : + enc = wxFONTENCODING_CP1250; + break ; + case kCFStringEncodingWindowsCyrillic : + enc = wxFONTENCODING_CP1251; + break ; + case kCFStringEncodingWindowsLatin1 : + enc = wxFONTENCODING_CP1252; + break ; + case kCFStringEncodingWindowsGreek : + enc = wxFONTENCODING_CP1253; + break ; + case kCFStringEncodingWindowsLatin5 : + enc = wxFONTENCODING_CP1254; + break ; + case kCFStringEncodingWindowsHebrew : + enc = wxFONTENCODING_CP1255; + break ; + case kCFStringEncodingWindowsArabic : + enc = wxFONTENCODING_CP1256; + break ; + case kCFStringEncodingWindowsBalticRim : + enc =wxFONTENCODING_CP1257 ; + break ; + case kCFStringEncodingEUC_JP : + enc = wxFONTENCODING_EUC_JP; + break ; + + case kCFStringEncodingUTF8 : + enc = wxFONTENCODING_UTF8; + break ; + case kCFStringEncodingUTF16BE : + enc = wxFONTENCODING_UTF16BE; + break ; + case kCFStringEncodingUTF16LE : + enc = wxFONTENCODING_UTF16LE; + break ; + case kCFStringEncodingUTF32BE : + enc = wxFONTENCODING_UTF32BE; + break ; + case kCFStringEncodingUTF32LE : + enc = wxFONTENCODING_UTF32LE; + break ; + +#if 0 + case wxFONTENCODING_UTF7 : + enc = CreateTextEncoding(kCFStringEncodingUnicodeDefault,0,kUnicodeUTF7Format) ; + break ; +#endif + case kCFStringEncodingMacRoman : + enc = wxFONTENCODING_MACROMAN ; + break ; + case kCFStringEncodingMacJapanese : + enc = wxFONTENCODING_MACJAPANESE ; + break ; + case kCFStringEncodingMacChineseTrad : + enc = wxFONTENCODING_MACCHINESETRAD ; + break ; + case kCFStringEncodingMacKorean : + enc = wxFONTENCODING_MACKOREAN ; + break ; + case kCFStringEncodingMacArabic : + enc =wxFONTENCODING_MACARABIC ; + break ; + case kCFStringEncodingMacHebrew : + enc = wxFONTENCODING_MACHEBREW ; + break ; + case kCFStringEncodingMacGreek : + enc = wxFONTENCODING_MACGREEK ; + break ; + case kCFStringEncodingMacCyrillic : + enc = wxFONTENCODING_MACCYRILLIC ; + break ; + case kCFStringEncodingMacDevanagari : + enc = wxFONTENCODING_MACDEVANAGARI ; + break ; + case kCFStringEncodingMacGurmukhi : + enc = wxFONTENCODING_MACGURMUKHI ; + break ; + case kCFStringEncodingMacGujarati : + enc = wxFONTENCODING_MACGUJARATI ; + break ; + case kCFStringEncodingMacOriya : + enc =wxFONTENCODING_MACORIYA ; + break ; + case kCFStringEncodingMacBengali : + enc =wxFONTENCODING_MACBENGALI ; + break ; + case kCFStringEncodingMacTamil : + enc = wxFONTENCODING_MACTAMIL ; + break ; + case kCFStringEncodingMacTelugu : + enc = wxFONTENCODING_MACTELUGU ; + break ; + case kCFStringEncodingMacKannada : + enc = wxFONTENCODING_MACKANNADA ; + break ; + case kCFStringEncodingMacMalayalam : + enc = wxFONTENCODING_MACMALAJALAM ; + break ; + case kCFStringEncodingMacSinhalese : + enc = wxFONTENCODING_MACSINHALESE ; + break ; + case kCFStringEncodingMacBurmese : + enc = wxFONTENCODING_MACBURMESE ; + break ; + case kCFStringEncodingMacKhmer : + enc = wxFONTENCODING_MACKHMER ; + break ; + case kCFStringEncodingMacThai : + enc = wxFONTENCODING_MACTHAI ; + break ; + case kCFStringEncodingMacLaotian : + enc = wxFONTENCODING_MACLAOTIAN ; + break ; + case kCFStringEncodingMacGeorgian : + enc = wxFONTENCODING_MACGEORGIAN ; + break ; + case kCFStringEncodingMacArmenian : + enc = wxFONTENCODING_MACARMENIAN ; + break ; + case kCFStringEncodingMacChineseSimp : + enc = wxFONTENCODING_MACCHINESESIMP ; + break ; + case kCFStringEncodingMacTibetan : + enc = wxFONTENCODING_MACTIBETAN ; + break ; + case kCFStringEncodingMacMongolian : + enc = wxFONTENCODING_MACMONGOLIAN ; + break ; + case kCFStringEncodingMacEthiopic : + enc = wxFONTENCODING_MACETHIOPIC ; + break ; + case kCFStringEncodingMacCentralEurRoman: + enc = wxFONTENCODING_MACCENTRALEUR ; + break ; + case kCFStringEncodingMacVietnamese: + enc = wxFONTENCODING_MACVIATNAMESE ; + break ; + case kCFStringEncodingMacExtArabic : + enc = wxFONTENCODING_MACARABICEXT ; + break ; + case kCFStringEncodingMacSymbol : + enc = wxFONTENCODING_MACSYMBOL ; + break ; + case kCFStringEncodingMacDingbats : + enc = wxFONTENCODING_MACDINGBATS ; + break ; + case kCFStringEncodingMacTurkish : + enc = wxFONTENCODING_MACTURKISH ; + break ; + case kCFStringEncodingMacCroatian : + enc = wxFONTENCODING_MACCROATIAN ; + break ; + case kCFStringEncodingMacIcelandic : + enc = wxFONTENCODING_MACICELANDIC ; + break ; + case kCFStringEncodingMacRomanian : + enc = wxFONTENCODING_MACROMANIAN ; + break ; + case kCFStringEncodingMacCeltic : + enc = wxFONTENCODING_MACCELTIC ; + break ; + case kCFStringEncodingMacGaelic : + enc = wxFONTENCODING_MACGAELIC ; + break ; + case 41 /* kTextEncodingMacKeyboardGlyphs */ : + enc = wxFONTENCODING_MACKEYBOARD ; + break ; + } ; + return enc ; +} + + +// +// CFStringRefs +// + +// converts this string into a core foundation string with optional pc 2 mac encoding + +wxCFStringRef::wxCFStringRef( const wxString &st , wxFontEncoding WXUNUSED_IN_UNICODE(encoding) ) +{ + if (st.IsEmpty()) + { + reset( wxCFRetain( CFSTR("") ) ); + } + else + { + wxString str = st ; + wxMacConvertNewlines13To10( &str ) ; +#if wxUSE_UNICODE +#if SIZEOF_WCHAR_T == 2 + reset( CFStringCreateWithCharacters( kCFAllocatorDefault, + (UniChar*)str.wc_str() , str.Len() ) ); +#else + wxMBConvUTF16 converter ; + size_t unicharbytes = converter.FromWChar( NULL , 0 , str.wc_str() , str.Length() ) ; + wxASSERT( unicharbytes != wxCONV_FAILED ); + if ( unicharbytes == wxCONV_FAILED ) + { + // create an empty string + reset( wxCFRetain( CFSTR("") ) ); + } + else + { + // unicharbytes: number of bytes needed for UTF-16 encoded string (without terminating null) + // unichars: number of UTF-16 characters (without terminating null) + size_t unichars = unicharbytes / sizeof(UniChar) ; + UniChar *unibuf = new UniChar[ unichars ] ; + converter.FromWChar( (char*)unibuf , unicharbytes , str.wc_str() , str.Length() ) ; + reset( CFStringCreateWithCharacters( kCFAllocatorDefault , unibuf , unichars ) ) ; + delete[] unibuf ; + } +#endif +#else // not wxUSE_UNICODE + reset( CFStringCreateWithCString( kCFAllocatorSystemDefault , str.c_str() , + wxMacGetSystemEncFromFontEnc( encoding ) ) ); +#endif + } +} + +wxString wxCFStringRef::AsString(wxFontEncoding WXUNUSED_IN_UNICODE(encoding)) +{ + if ( !get() ) + return wxEmptyString ; + + Size cflen = CFStringGetLength( get() ) ; + size_t noChars ; + wxChar* buf = NULL ; + +#if wxUSE_UNICODE +#if SIZEOF_WCHAR_T == 2 + buf = new wxChar[ cflen + 1 ] ; + CFStringGetCharacters( get() , CFRangeMake( 0 , cflen ) , (UniChar*) buf ) ; + noChars = cflen ; +#else + UniChar* unibuf = new UniChar[ cflen + 1 ] ; + CFStringGetCharacters( get() , CFRangeMake( 0 , cflen ) , (UniChar*) unibuf ) ; + unibuf[cflen] = 0 ; + wxMBConvUTF16 converter ; + noChars = converter.MB2WC( NULL , (const char*)unibuf , 0 ) ; + wxASSERT_MSG( noChars != wxCONV_FAILED, _T("Unable to count the number of characters in this string!") ); + buf = new wxChar[ noChars + 1 ] ; + noChars = converter.MB2WC( buf , (const char*)unibuf , noChars + 1 ) ; + wxASSERT_MSG( noChars != wxCONV_FAILED, _T("Conversion of string failed!") ); + delete[] unibuf ; +#endif +#else + CFIndex cStrLen ; + CFStringGetBytes( get() , CFRangeMake(0, cflen) , wxMacGetSystemEncFromFontEnc( encoding ) , + '?' , false , NULL , 0 , &cStrLen ) ; + buf = new wxChar[ cStrLen + 1 ] ; + CFStringGetBytes( get() , CFRangeMake(0, cflen) , wxMacGetSystemEncFromFontEnc( encoding ) , + '?' , false , (unsigned char*) buf , cStrLen , &cStrLen) ; + noChars = cStrLen ; +#endif + + buf[noChars] = 0 ; + wxString result(buf) ; + delete[] buf ; + wxMacConvertNewlines10To13( &result); + return result ; +} + +// +// wxMacUniCharBuffer +// + +wxMacUniCharBuffer::wxMacUniCharBuffer( const wxString &str ) +{ + m_chars = str.length() ; + m_ubuf = NULL ; + +#if SIZEOF_WCHAR_T == 4 + wxMBConvUTF16 converter ; +#if wxUSE_UNICODE + size_t unicharlen = converter.WC2MB( NULL , str.wc_str() , 0 ) ; + m_ubuf = (UniChar*) malloc( unicharlen + 2 ) ; + converter.WC2MB( (char*) m_ubuf , str.wc_str(), unicharlen + 2 ) ; +#else + const wxWCharBuffer wchar = str.wc_str( wxConvLocal ) ; + size_t unicharlen = converter.WC2MB( NULL , wchar.data() , 0 ) ; + m_ubuf = (UniChar*) malloc( unicharlen + 2 ) ; + converter.WC2MB( (char*) m_ubuf , wchar.data() , unicharlen + 2 ) ; +#endif + m_chars = unicharlen / 2 ; +#else // SIZEOF_WCHAR_T is then 2 +#if wxUSE_UNICODE + m_ubuf = malloc( m_chars * 2 + 2 ) ; + memcpy( m_ubuf , (UniChar*) str.wc_str() , m_chars * 2 + 2 ) ; +#else + wxWCharBuffer wchar = str.wc_str( wxConvLocal ) ; + m_chars = wxWcslen( wchar.data() ) ; + m_ubuf = malloc( m_chars * 2 + 2 ) ; + memcpy( m_ubuf , (UniChar*) wchar.data() , m_chars * 2 + 2 ) ; +#endif +#endif +} + +wxMacUniCharBuffer::~wxMacUniCharBuffer() +{ + free( m_ubuf ) ; +} + +UniCharPtr wxMacUniCharBuffer::GetBuffer() +{ + return m_ubuf ; +} + +UniCharCount wxMacUniCharBuffer::GetChars() +{ + return m_chars ; +} diff --git a/src/osx/core/gsockosx.cpp b/src/osx/core/gsockosx.cpp new file mode 100644 index 0000000000..695dab74ca --- /dev/null +++ b/src/osx/core/gsockosx.cpp @@ -0,0 +1,318 @@ +/* ------------------------------------------------------------------------- + * Project: GSocket (Generic Socket) for WX + * Name: src/mac/corefoundation/gsockosx.c + * Purpose: GSocket: Mac OS X mach-o part + * CVSID: $Id$ + * Mac code by Brian Victor, February 2002. Email comments to bhv1@psu.edu + * ------------------------------------------------------------------------- */ + +#include "wx/wxprec.h" + +#if wxUSE_SOCKETS + +#include "wx/gsocket.h" +#include "wx/apptrait.h" + +#include + +// ---------------------------------------------------------------------------- +// Mac-specific data associated with each socket by GSocketCFManager +// ---------------------------------------------------------------------------- + +class MacGSocketData +{ +public: + // default ctor creates the object in uninitialized state, use Initialize() + // later to make it usable + MacGSocketData() + { + m_socket = NULL; + m_source = NULL; + } + + // initialize the data associated with the given socket + bool Initialize(GSocket *socket) + { + wxASSERT_MSG( !IsInitialized(), "shouldn't be called twice" ); + + // we need a valid Unix socket to create a CFSocket + if ( socket->m_fd < 0 ) + return false; + + CFSocketContext cont; + cont.version = 0; // this currently must be 0 + cont.info = socket; // pointer passed to our callback + cont.retain = NULL; // no need to retain/release/copy the + cont.release = NULL; // socket pointer, so all callbacks + cont.copyDescription = NULL; // can be left NULL + + m_socket = CFSocketCreateWithNative + ( + NULL, // default allocator + socket->m_fd, + kCFSocketReadCallBack | + kCFSocketWriteCallBack | + kCFSocketConnectCallBack, + SocketCallback, + &cont + ); + if ( !m_socket ) + return false; + + m_source = CFSocketCreateRunLoopSource(NULL, m_socket, 0); + + return m_source != NULL; + } + + // free the objects created by Initialize() + ~MacGSocketData() + { + if ( m_source ) + CFRelease(m_source); + if ( m_socket ) + CFRelease(m_socket); + } + + // return true if Initialize() had already been called successfully + bool IsInitialized() const { return m_source && m_socket; } + + + // accessors: should only be called if IsInitialized() + CFSocketRef GetSocket() const + { + wxASSERT( IsInitialized() ); + + return m_socket; + } + + CFRunLoopSourceRef GetSource() const + { + wxASSERT( IsInitialized() ); + + return m_source; + } + +private: + static void SocketCallback(CFSocketRef WXUNUSED(s), + CFSocketCallBackType callbackType, + CFDataRef WXUNUSED(address), + const void* data, + void* info) + { + GSocket * const socket = wx_static_cast(GSocket *, info); + MacGSocketData * const + macdata = wx_static_cast(MacGSocketData *, socket->m_gui_dependent); + if ( !macdata ) + return; + + switch (callbackType) + { + case kCFSocketConnectCallBack: + wxASSERT(!socket->m_server); + // KH: If data is non-NULL, the connect failed, do not call Detected_Write, + // which will only end up creating a spurious connect event because the + // call to getsocketopt SO_ERROR inexplicably returns no error. + // The change in behavior cannot be traced to any particular commit or + // timeframe so I'm not sure what to think, but after so many hours, + // this seems to address the issue and it's time to move on. + if (data == NULL) + socket->Detected_Write(); + break; + + case kCFSocketReadCallBack: + socket->Detected_Read(); + break; + + case kCFSocketWriteCallBack: + socket->Detected_Write(); + break; + + default: + wxFAIL_MSG( "unexpected socket callback" ); + } + } + + CFSocketRef m_socket; + CFRunLoopSourceRef m_source; + + DECLARE_NO_COPY_CLASS(MacGSocketData); +}; + +// ---------------------------------------------------------------------------- +// CoreFoundation implementation of GSocketManager +// ---------------------------------------------------------------------------- + +class GSocketCFManager : public GSocketManager +{ +public: + virtual bool OnInit(); + virtual void OnExit(); + + virtual bool Init_Socket(GSocket *socket); + virtual void Destroy_Socket(GSocket *socket); + + virtual void Install_Callback(GSocket *socket, GSocketEvent event); + virtual void Uninstall_Callback(GSocket *socket, GSocketEvent event); + + virtual void Enable_Events(GSocket *socket); + virtual void Disable_Events(GSocket *socket); + +private: + // retrieve our custom data associated with the given socket + // + // this is a low level function, use GetInitializedData() instead if the + // data pointer should also be correctly initialized if it hadn't been done + // yet + // + // may return NULL if we hadn't created the data for this socket yet + MacGSocketData *GetData(GSocket *socket) const + { + return wx_static_cast(MacGSocketData *, socket->m_gui_dependent); + } + + // return the custom data pointer initializing it if it hadn't been done + // yet + // + // may return NULL if there is no associated data + MacGSocketData *GetInitializedData(GSocket *socket) const + { + MacGSocketData * const data = GetData(socket); + if ( data && !data->IsInitialized() ) + { + if ( !data->Initialize(socket) ) + return NULL; + } + + return data; + } + + // return CFSocket callback mask corresponding to the given event (the + // socket parameter is needed because some events are interpreted + // differently depending on whether they happen on a server or on a client + // socket) + static int GetCFCallback(GSocket *socket, GSocketEvent event); + + + // Sockets must use the event loop on the main thread so we store a + // reference to the main loop here in OnInit() + static CFRunLoopRef ms_mainRunLoop; +}; + +CFRunLoopRef GSocketCFManager::ms_mainRunLoop = NULL; + +bool GSocketCFManager::OnInit() +{ + // No need to store the main loop again + if (ms_mainRunLoop != NULL) + return true; + + // Get the loop for the main thread so our events will actually fire. + // The common socket.cpp code will assert if initialize is called from a + // secondary thread, otherwise Mac would have the same problems as MSW + ms_mainRunLoop = CFRunLoopGetCurrent(); + if ( !ms_mainRunLoop ) + return false; + + CFRetain(ms_mainRunLoop); + + return true; +} + +void GSocketCFManager::OnExit() +{ + // Release the reference count, and set the reference back to NULL + CFRelease(ms_mainRunLoop); + ms_mainRunLoop = NULL; +} + +bool GSocketCFManager::Init_Socket(GSocket *socket) +{ + socket->m_gui_dependent = new MacGSocketData; + return true; +} + +void GSocketCFManager::Destroy_Socket(GSocket *socket) +{ + MacGSocketData * const data = GetData(socket); + if ( data ) + { + delete data; + socket->m_gui_dependent = NULL; + } +} + +/* static */ +int GSocketCFManager::GetCFCallback(GSocket *socket, GSocketEvent event) +{ + switch ( event ) + { + case GSOCK_CONNECTION: + return socket->m_server ? kCFSocketReadCallBack + : kCFSocketConnectCallBack; + + case GSOCK_LOST: + case GSOCK_INPUT: + return kCFSocketReadCallBack; + + case GSOCK_OUTPUT: + return kCFSocketWriteCallBack; + + case GSOCK_MAX_EVENT: + wxFAIL_MSG( "invalid GSocketEvent" ); + return 0; + + default: + wxFAIL_MSG( "unknown GSocketEvent" ); + return 0; + } +} + +void GSocketCFManager::Install_Callback(GSocket *socket, GSocketEvent event) +{ + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; + + CFSocketEnableCallBacks(data->GetSocket(), GetCFCallback(socket, event)); +} + +void GSocketCFManager::Uninstall_Callback(GSocket *socket, GSocketEvent event) +{ + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; + + CFSocketDisableCallBacks(data->GetSocket(), GetCFCallback(socket, event)); +} + +void GSocketCFManager::Enable_Events(GSocket *socket) +{ + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; + + CFRunLoopAddSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes); +} + +void GSocketCFManager::Disable_Events(GSocket *socket) +{ + const MacGSocketData * const data = GetInitializedData(socket); + if ( !data ) + return; + + // CFSocketInvalidate does CFRunLoopRemoveSource anyway + CFRunLoopRemoveSource(ms_mainRunLoop, data->GetSource(), kCFRunLoopCommonModes); + CFSocketInvalidate(data->GetSocket()); + + // CFSocketInvalidate has closed the socket so we want to make sure GSocket knows this + socket->m_fd = -1; +} + +GSocketManager *wxAppTraits::GetSocketManager() +{ + static GSocketCFManager s_manager; + + return &s_manager; +}; + +#endif // wxUSE_SOCKETS diff --git a/src/osx/core/hid.cpp b/src/osx/core/hid.cpp new file mode 100644 index 0000000000..fb9a4d051d --- /dev/null +++ b/src/osx/core/hid.cpp @@ -0,0 +1,741 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/corefoundation/hid.cpp +// Purpose: DARWIN HID layer for WX Implementation +// Author: Ryan Norton +// Modified by: +// Created: 11/11/2003 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// =========================================================================== +// declarations +// =========================================================================== + +// --------------------------------------------------------------------------- +// headers +// --------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +//DARWIN _ONLY_ +#ifdef __DARWIN__ + +#include "wx/mac/corefoundation/hid.h" + +#ifndef WX_PRECOMP + #include "wx/dynarray.h" + #include "wx/string.h" + #include "wx/log.h" + #include "wx/utils.h" + #include "wx/module.h" +#endif + +#include "wx/mac/corefoundation/cfstring.h" + +// ============================================================================ +// implementation +// ============================================================================ + +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxHIDDevice +// +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +// ---------------------------------------------------------------------------- +// wxHIDDevice::Create +// +// nClass is the HID Page such as +// kHIDPage_GenericDesktop +// nType is the HID Usage such as +// kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard +// nDev is the device number to use +// +// ---------------------------------------------------------------------------- +bool wxHIDDevice::Create (int nClass, int nType, int nDev) +{ + //Create the mach port + if(IOMasterPort(bootstrap_port, &m_pPort) != kIOReturnSuccess) + { + wxLogSysError(wxT("Could not create mach port")); + return false; + } + + //Dictionary that will hold first + //the matching dictionary for determining which kind of devices we want, + //then later some registry properties from an iterator (see below) + // + //The call to IOServiceMatching filters down the + //the services we want to hid services (and also eats the + //dictionary up for us (consumes one reference)) + CFMutableDictionaryRef pDictionary = IOServiceMatching(kIOHIDDeviceKey); + if(pDictionary == NULL) + { + wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") ); + return false; + } + + //Here we'll filter down the services to what we want + if (nType != -1) + { + CFNumberRef pType = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, &nType); + CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsageKey), pType); + CFRelease(pType); + } + if (nClass != -1) + { + CFNumberRef pClass = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, &nClass); + CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsagePageKey), pClass); + CFRelease(pClass); + } + + //Now get the maching services + io_iterator_t pIterator; + if( IOServiceGetMatchingServices(m_pPort, + pDictionary, &pIterator) != kIOReturnSuccess ) + { + wxLogSysError(_T("No Matching HID Services")); + return false; + } + + //Were there any devices matched? + if(pIterator == 0) + return false; // No devices found + + //Now we iterate through them + io_object_t pObject; + while ( (pObject = IOIteratorNext(pIterator)) != 0) + { + if(--nDev != 0) + { + IOObjectRelease(pObject); + continue; + } + + if ( IORegistryEntryCreateCFProperties + ( + pObject, + &pDictionary, + kCFAllocatorDefault, + kNilOptions + ) != KERN_SUCCESS ) + { + wxLogDebug(_T("IORegistryEntryCreateCFProperties failed")); + } + + // + // Now we get the attributes of each "product" in the iterator + // + + //Get [product] name + CFStringRef cfsProduct = (CFStringRef) + CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductKey)); + m_szProductName = + wxCFStringRef( wxCFRetain(cfsProduct) + ).AsString(); + + //Get the Product ID Key + CFNumberRef cfnProductId = (CFNumberRef) + CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductIDKey)); + if (cfnProductId) + { + CFNumberGetValue(cfnProductId, kCFNumberIntType, &m_nProductId); + } + + //Get the Vendor ID Key + CFNumberRef cfnVendorId = (CFNumberRef) + CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDVendorIDKey)); + if (cfnVendorId) + { + CFNumberGetValue(cfnVendorId, kCFNumberIntType, &m_nManufacturerId); + } + + // + // End attribute getting + // + + //Create the interface (good grief - long function names!) + SInt32 nScore; + IOCFPlugInInterface** ppPlugin; + if(IOCreatePlugInInterfaceForService(pObject, + kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &ppPlugin, + &nScore) != kIOReturnSuccess) + { + wxLogSysError(wxT("Could not create HID Interface for product")); + return false; + } + + //Now, the final thing we can check before we fall back to asserts + //(because the dtor only checks if the device is ok, so if anything + //fails from now on the dtor will delete the device anyway, so we can't break from this). + + //Get the HID interface from the plugin to the mach port + if((*ppPlugin)->QueryInterface(ppPlugin, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), + (void**) &m_ppDevice) != S_OK) + { + wxLogSysError(wxT("Could not get device interface from HID interface")); + return false; + } + + //release the plugin + (*ppPlugin)->Release(ppPlugin); + + //open the HID interface... + if ( (*m_ppDevice)->open(m_ppDevice, 0) != S_OK ) + wxLogDebug(_T("HID device: open failed")); + + // + //Now the hard part - in order to scan things we need "cookies" + // + CFArrayRef cfaCookies = (CFArrayRef)CFDictionaryGetValue(pDictionary, + CFSTR(kIOHIDElementKey)); + BuildCookies(cfaCookies); + + //cleanup + CFRelease(pDictionary); + IOObjectRelease(pObject); + + //iterator cleanup + IOObjectRelease(pIterator); + + return true; + } + + //iterator cleanup + IOObjectRelease(pIterator); + + return false; //no device +}//end Create() + +// ---------------------------------------------------------------------------- +// wxHIDDevice::GetCount [static] +// +// Obtains the number of devices on a system for a given HID Page (nClass) +// and HID Usage (nType). +// ---------------------------------------------------------------------------- +size_t wxHIDDevice::GetCount (int nClass, int nType) +{ + //Create the mach port + mach_port_t pPort; + if(IOMasterPort(bootstrap_port, &pPort) != kIOReturnSuccess) + { + wxLogSysError(wxT("Could not create mach port")); + return false; + } + + //Dictionary that will hold first + //the matching dictionary for determining which kind of devices we want, + //then later some registry properties from an iterator (see below) + CFMutableDictionaryRef pDictionary = IOServiceMatching(kIOHIDDeviceKey); + if(pDictionary == NULL) + { + wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") ); + return false; + } + + //Here we'll filter down the services to what we want + if (nType != -1) + { + CFNumberRef pType = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, &nType); + CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsageKey), pType); + CFRelease(pType); + } + if (nClass != -1) + { + CFNumberRef pClass = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, &nClass); + CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsagePageKey), pClass); + CFRelease(pClass); + } + + //Now get the maching services + io_iterator_t pIterator; + if( IOServiceGetMatchingServices(pPort, + pDictionary, &pIterator) != kIOReturnSuccess ) + { + wxLogSysError(_T("No Matching HID Services")); + return false; + } + + //If the iterator doesn't exist there are no devices :) + if ( !pIterator ) + return 0; + + //Now we iterate through them + size_t nCount = 0; + io_object_t pObject; + while ( (pObject = IOIteratorNext(pIterator)) != 0) + { + ++nCount; + IOObjectRelease(pObject); + } + + //cleanup + IOObjectRelease(pIterator); + mach_port_deallocate(mach_task_self(), pPort); + + return nCount; +}//end Create() + +// ---------------------------------------------------------------------------- +// wxHIDDevice::AddCookie +// +// Adds a cookie to the internal cookie array from a CFType +// ---------------------------------------------------------------------------- +void wxHIDDevice::AddCookie(CFTypeRef Data, int i) +{ + CFNumberGetValue( + (CFNumberRef) CFDictionaryGetValue ( (CFDictionaryRef) Data + , CFSTR(kIOHIDElementCookieKey) + ), + kCFNumberIntType, + &m_pCookies[i] + ); +} + +// ---------------------------------------------------------------------------- +// wxHIDDevice::AddCookieInQueue +// +// Adds a cookie to the internal cookie array from a CFType and additionally +// adds it to the internal HID Queue +// ---------------------------------------------------------------------------- +void wxHIDDevice::AddCookieInQueue(CFTypeRef Data, int i) +{ + //3rd Param flags (none yet) + AddCookie(Data, i); + if ( (*m_ppQueue)->addElement(m_ppQueue, m_pCookies[i], 0) != S_OK ) + wxLogDebug(_T("HID device: adding element failed")); +} + +// ---------------------------------------------------------------------------- +// wxHIDDevice::InitCookies +// +// Create the internal cookie array, optionally creating a HID Queue +// ---------------------------------------------------------------------------- +void wxHIDDevice::InitCookies(size_t dwSize, bool bQueue) +{ + m_pCookies = new IOHIDElementCookie[dwSize]; + if (bQueue) + { + wxASSERT( m_ppQueue == NULL); + m_ppQueue = (*m_ppDevice)->allocQueue(m_ppDevice); + if ( !m_ppQueue ) + { + wxLogDebug(_T("HID device: allocQueue failed")); + return; + } + + //Param 2, flags, none yet + if ( (*m_ppQueue)->create(m_ppQueue, 0, 512) != S_OK ) + { + wxLogDebug(_T("HID device: create failed")); + } + } + + //make sure that cookie array is clear + memset(m_pCookies, 0, sizeof(*m_pCookies) * dwSize); +} + +// ---------------------------------------------------------------------------- +// wxHIDDevice::IsActive +// +// Returns true if a cookie of the device is active - for example if a key is +// held down, joystick button pressed, caps lock active, etc.. +// ---------------------------------------------------------------------------- +bool wxHIDDevice::IsActive(int nIndex) +{ + if(!HasElement(nIndex)) + { + //cookie at index does not exist - getElementValue + //could return true which would be incorrect so we + //check here + return false; + } + + IOHIDEventStruct Event; + (*m_ppDevice)->getElementValue(m_ppDevice, m_pCookies[nIndex], &Event); + return !!Event.value; +} + +// ---------------------------------------------------------------------------- +// wxHIDDevice::HasElement +// +// Returns true if the element in the internal cookie array exists +// ---------------------------------------------------------------------------- +bool wxHIDDevice::HasElement(int nIndex) +{ + return m_pCookies[nIndex] != NULL; +} + +// ---------------------------------------------------------------------------- +// wxHIDDevice Destructor +// +// Frees all memory and objects from the structure +// ---------------------------------------------------------------------------- +wxHIDDevice::~wxHIDDevice() +{ + if (m_ppDevice != NULL) + { + if (m_ppQueue != NULL) + { + (*m_ppQueue)->stop(m_ppQueue); + (*m_ppQueue)->dispose(m_ppQueue); + (*m_ppQueue)->Release(m_ppQueue); + } + (*m_ppDevice)->close(m_ppDevice); + (*m_ppDevice)->Release(m_ppDevice); + mach_port_deallocate(mach_task_self(), m_pPort); + } + + if (m_pCookies != NULL) + { + delete [] m_pCookies; + } +} + +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxHIDKeyboard +// +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//There are no right shift, alt etc. in the wx headers yet so just sort +//of "define our own" for now +enum +{ + WXK_RSHIFT = 400, + WXK_RALT, + WXK_RCONTROL, + WXK_RMENU +}; + +// ---------------------------------------------------------------------------- +// wxHIDKeyboard::GetCount [static] +// +// Get number of HID keyboards available +// ---------------------------------------------------------------------------- +int wxHIDKeyboard::GetCount() +{ + return wxHIDDevice::GetCount(kHIDPage_GenericDesktop, + kHIDUsage_GD_Keyboard); +} + +// ---------------------------------------------------------------------------- +// wxHIDKeyboard::Create +// +// Create the HID Keyboard +// ---------------------------------------------------------------------------- +bool wxHIDKeyboard::Create(int nDev /* = 1*/) +{ + return wxHIDDevice::Create(kHIDPage_GenericDesktop, + kHIDUsage_GD_Keyboard, + nDev); +} + +// ---------------------------------------------------------------------------- +// wxHIDKeyboard::AddCookie +// +// Overloaded version of wxHIDDevice::AddCookie that simply does not +// add a cookie if a duplicate is found +// ---------------------------------------------------------------------------- +void wxHIDKeyboard::AddCookie(CFTypeRef Data, int i) +{ + if(!HasElement(i)) + wxHIDDevice::AddCookie(Data, i); +} + +// ---------------------------------------------------------------------------- +// wxHIDKeyboard::BuildCookies +// +// Callback from Create() to build the HID cookies for the internal cookie +// array +// ---------------------------------------------------------------------------- +void wxHIDKeyboard::BuildCookies(CFArrayRef Array) +{ + //Create internal cookie array + InitCookies(500); + + //Begin recursing in array + DoBuildCookies(Array); +} + +void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array) +{ + //Now go through each possible cookie + int i, + nUsage; +// bool bEOTriggered = false; + for (i = 0; i < CFArrayGetCount(Array); ++i) + { + const void* ref = CFDictionaryGetValue( + (CFDictionaryRef)CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementKey) + ); + + if (ref != NULL) + { + DoBuildCookies((CFArrayRef) ref); + } + else + { + + // + // Get the usage # + // + CFNumberGetValue( + (CFNumberRef) + CFDictionaryGetValue((CFDictionaryRef) + CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementUsageKey) + ), + kCFNumberLongType, + &nUsage); + + // + // Now translate the usage # into a wx keycode + // + + // + // OK, this is strange - basically this kind of strange - + // Starting from 0xEO these elements (like shift) appear twice in + // the array! The ones at the end are bogus I guess - the funny part + // is that besides the fact that the ones at the front have a Unit + // and UnitExponent key with a value of 0 and a different cookie value, + // there is no discernable difference between the two... + // + // Will the real shift please stand up? + // + // Something to spend a support request on, if I had one, LOL. + // + //if(nUsage == 0xE0) + //{ + // if(bEOTriggered) + // break; + // bEOTriggered = true; + //} + //Instead of that though we now just don't add duplicate keys + + if (nUsage >= kHIDUsage_KeyboardA && nUsage <= kHIDUsage_KeyboardZ) + AddCookie(CFArrayGetValueAtIndex(Array, i), 'A' + (nUsage - kHIDUsage_KeyboardA) ); + else if (nUsage >= kHIDUsage_Keyboard1 && nUsage <= kHIDUsage_Keyboard9) + AddCookie(CFArrayGetValueAtIndex(Array, i), '1' + (nUsage - kHIDUsage_Keyboard1) ); + else if (nUsage >= kHIDUsage_KeyboardF1 && nUsage <= kHIDUsage_KeyboardF12) + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_F1 + (nUsage - kHIDUsage_KeyboardF1) ); + else if (nUsage >= kHIDUsage_KeyboardF13 && nUsage <= kHIDUsage_KeyboardF24) + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_F13 + (nUsage - kHIDUsage_KeyboardF13) ); + else if (nUsage >= kHIDUsage_Keypad1 && nUsage <= kHIDUsage_Keypad9) + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_NUMPAD1 + (nUsage - kHIDUsage_Keypad1) ); + else switch (nUsage) + { + //0's (wx & ascii go 0-9, but HID goes 1-0) + case kHIDUsage_Keyboard0: + AddCookie(CFArrayGetValueAtIndex(Array, i), '0'); + break; + case kHIDUsage_Keypad0: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_NUMPAD0); + break; + + //Basic + case kHIDUsage_KeyboardReturnOrEnter: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_RETURN); + break; + case kHIDUsage_KeyboardEscape: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_ESCAPE); + break; + case kHIDUsage_KeyboardDeleteOrBackspace: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_BACK); + break; + case kHIDUsage_KeyboardTab: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_TAB); + break; + case kHIDUsage_KeyboardSpacebar: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_SPACE); + break; + case kHIDUsage_KeyboardPageUp: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_PAGEUP); + break; + case kHIDUsage_KeyboardEnd: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_END); + break; + case kHIDUsage_KeyboardPageDown: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_PAGEDOWN); + break; + case kHIDUsage_KeyboardRightArrow: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_RIGHT); + break; + case kHIDUsage_KeyboardLeftArrow: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_LEFT); + break; + case kHIDUsage_KeyboardDownArrow: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_DOWN); + break; + case kHIDUsage_KeyboardUpArrow: + AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_UP); + break; + + //LEDS + case kHIDUsage_KeyboardCapsLock: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_CAPITAL); + break; + case kHIDUsage_KeypadNumLock: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_NUMLOCK); + break; + case kHIDUsage_KeyboardScrollLock: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_SCROLL); + break; + + //Menu keys, Shift, other specials + case kHIDUsage_KeyboardLeftControl: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_CONTROL); + break; + case kHIDUsage_KeyboardLeftShift: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_SHIFT); + break; + case kHIDUsage_KeyboardLeftAlt: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_ALT); + break; + case kHIDUsage_KeyboardLeftGUI: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_MENU); + break; + case kHIDUsage_KeyboardRightControl: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RCONTROL); + break; + case kHIDUsage_KeyboardRightShift: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RSHIFT); + break; + case kHIDUsage_KeyboardRightAlt: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RALT); + break; + case kHIDUsage_KeyboardRightGUI: + AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RMENU); + break; + + //Default + default: + //not in wx keycodes - do nothing.... + break; + } //end mightly long switch + } //end if the current element is not an array... + } //end for loop for Array +}//end buildcookies + +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxHIDModule +// +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +class wxHIDModule : public wxModule +{ + DECLARE_DYNAMIC_CLASS(wxHIDModule) + + public: + static wxArrayPtrVoid sm_keyboards; + virtual bool OnInit() + { + return true; + } + virtual void OnExit() + { + for(size_t i = 0; i < sm_keyboards.GetCount(); ++i) + delete (wxHIDKeyboard*) sm_keyboards[i]; + } +}; + +IMPLEMENT_DYNAMIC_CLASS(wxHIDModule, wxModule) + +wxArrayPtrVoid wxHIDModule::sm_keyboards; + +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxGetKeyState() +// +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +bool wxGetKeyState (wxKeyCode key) +{ + wxASSERT_MSG(key != WXK_LBUTTON && key != WXK_RBUTTON && key != + WXK_MBUTTON, wxT("can't use wxGetKeyState() for mouse buttons")); + + if (wxHIDModule::sm_keyboards.GetCount() == 0) + { + int nKeyboards = wxHIDKeyboard::GetCount(); + + for(int i = 1; i <= nKeyboards; ++i) + { + wxHIDKeyboard* keyboard = new wxHIDKeyboard(); + if(keyboard->Create(i)) + { + wxHIDModule::sm_keyboards.Add(keyboard); + } + else + { + delete keyboard; + break; + } + } + + wxASSERT_MSG(wxHIDModule::sm_keyboards.GetCount() != 0, + wxT("No keyboards found!")); + } + + for(size_t i = 0; i < wxHIDModule::sm_keyboards.GetCount(); ++i) + { + wxHIDKeyboard* keyboard = (wxHIDKeyboard*) + wxHIDModule::sm_keyboards[i]; + + switch(key) + { + case WXK_SHIFT: + if( keyboard->IsActive(WXK_SHIFT) || + keyboard->IsActive(WXK_RSHIFT) ) + { + return true; + } + break; + case WXK_ALT: + if( keyboard->IsActive(WXK_ALT) || + keyboard->IsActive(WXK_RALT) ) + { + return true; + } + break; + case WXK_CONTROL: + if( keyboard->IsActive(WXK_CONTROL) || + keyboard->IsActive(WXK_RCONTROL) ) + { + return true; + } + break; + case WXK_MENU: + if( keyboard->IsActive(WXK_MENU) || + keyboard->IsActive(WXK_RMENU) ) + { + return true; + } + break; + default: + if( keyboard->IsActive(key) ) + { + return true; + } + break; + } + } + + return false; //not down/error +} + +#endif //__DARWIN__ diff --git a/src/osx/core/hidjoystick.cpp b/src/osx/core/hidjoystick.cpp new file mode 100644 index 0000000000..637546c414 --- /dev/null +++ b/src/osx/core/hidjoystick.cpp @@ -0,0 +1,905 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/corefoundation/joystick.cpp +// Purpose: wxJoystick class +// Author: Ryan Norton +// Modified by: +// Created: 2/13/2005 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +//=========================================================================== +// DECLARATIONS +//=========================================================================== + +//--------------------------------------------------------------------------- +// Pre-compiled header stuff +//--------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +//--------------------------------------------------------------------------- +// Guard +//--------------------------------------------------------------------------- + +//we only support HID on OSX (DARWIN), since it requires DARWIN... +#if wxUSE_JOYSTICK && wxUSE_THREADS + +//--------------------------------------------------------------------------- +// Includes +//--------------------------------------------------------------------------- + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/event.h" //joystick wxEvents + #include "wx/window.h" //for wxWindow to "capture" joystick +#endif + +#include "wx/joystick.h" //... +#include "wx/thread.h" //wxThread for polling thread/ wxCriticalSection + +//private headers +#include "wx/mac/corefoundation/hid.h" //private mac hid stuff + +//mac headers +#include +#include +#include +#include + +//--------------------------------------------------------------------------- +// Definitions/Enumerations +//--------------------------------------------------------------------------- + +#define wxJS_MAX_AXES 10 /*max number of axes*/ +#define wxJS_MAX_BUTTONS 40 /*max number of buttons*/ + +enum +{ + //These are positions within the cookie array + //in wxHIDJoystick that the cookies that store the axis' are + wxJS_AXIS_X = 40, + wxJS_AXIS_Y, + wxJS_AXIS_Z, + wxJS_AXIS_RUDDER, + wxJS_AXIS_U, + wxJS_AXIS_V, +}; + +//--------------------------------------------------------------------------- +// wxHIDJoystick +//--------------------------------------------------------------------------- +class wxHIDJoystick : public wxHIDDevice +{ +public: + wxHIDJoystick(); + virtual ~wxHIDJoystick(); + + bool Create(int nWhich); + virtual void BuildCookies(CFArrayRef Array); + void MakeCookies(CFArrayRef Array); + IOHIDElementCookie* GetCookies(); + IOHIDQueueInterface** GetQueue(); + + int m_nXMax, m_nYMax, m_nZMax, m_nRudderMax, m_nUMax, m_nVMax, + m_nXMin, m_nYMin, m_nZMin, m_nRudderMin, m_nUMin, m_nVMin; + + friend class wxJoystick; +}; + +//--------------------------------------------------------------------------- +// wxJoystickThread +//--------------------------------------------------------------------------- +class wxJoystickThread : public wxThread +{ +public: + wxJoystickThread(wxHIDJoystick* hid, int joystick); + void* Entry(); + static void HIDCallback(void* target, IOReturn res, void* context, void* sender); + +private: + wxHIDJoystick* m_hid; + int m_joystick; + wxPoint m_lastposition; + int m_axe[wxJS_MAX_AXES]; + int m_buttons; + wxWindow* m_catchwin; + int m_polling; + + friend class wxJoystick; +}; + +//=========================================================================== +// IMPLEMENTATION +//=========================================================================== + +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +// wxGetIntFromCFDictionary +// +// Helper function that gets a integer from a dictionary key +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void wxGetIntFromCFDictionary(CFTypeRef cfDict, CFStringRef key, int* pOut) +{ + CFNumberGetValue( + (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) cfDict, + key), + kCFNumberIntType, pOut); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxJoystick +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject) + +//--------------------------------------------------------------------------- +// wxJoystick Constructor +// +// 1) Initializes member variables +// 2) Attempts to create the native HID joystick implementation - if none +// could be found (no joysticks, etc.) then it sets it to NULL +//--------------------------------------------------------------------------- +wxJoystick::wxJoystick(int joystick) + : m_joystick(joystick), + m_thread(NULL) +{ + m_hid = new wxHIDJoystick(); + + if (m_hid->Create(m_joystick+1)) //wxHIDDevice is 1-based while this is 0 + { + m_thread = new wxJoystickThread(m_hid, m_joystick); + m_thread->Create(); + m_thread->Run(); + } + else + { + delete m_hid; + m_hid = NULL; + } +} + +//--------------------------------------------------------------------------- +// wxJoystick Destructor +// +// Releases the capture of the thread, deletes it, and deletes +// the native implementation. +//--------------------------------------------------------------------------- +wxJoystick::~wxJoystick() +{ + ReleaseCapture(); + if (m_thread) + m_thread->Delete(); // It's detached so it will delete itself + + if (m_hid) + delete m_hid; +} + +//--------------------------------------------------------------------------- +// wxJoystick::Get[XXX]Position +// +// Returns the value of an axis that was polled from the thread. In the +// case of GetPosition returns the X and Y values in a wxPoint +//--------------------------------------------------------------------------- +wxPoint wxJoystick::GetPosition() const +{ + wxPoint pos(wxDefaultPosition); + if (m_thread) pos = m_thread->m_lastposition; + return pos; +} +int wxJoystick::GetZPosition() const +{ + if (m_thread) + return m_thread->m_axe[wxJS_AXIS_Z]; + return 0; +} +int wxJoystick::GetRudderPosition() const +{ + if (m_thread) + return m_thread->m_axe[wxJS_AXIS_RUDDER]; + return 0; +} +int wxJoystick::GetUPosition() const +{ + if (m_thread) + return m_thread->m_axe[wxJS_AXIS_U]; + return 0; +} +int wxJoystick::GetVPosition() const +{ + if (m_thread) + return m_thread->m_axe[wxJS_AXIS_V]; + return 0; +} + +//--------------------------------------------------------------------------- +// wxJoystick::GetButtonState +// +// Returns the state of the buttons in a bitmask as dictated by the +// wx manual (the real work takes place in the thread, as always) +//--------------------------------------------------------------------------- +int wxJoystick::GetButtonState() const +{ + if (m_thread) + return m_thread->m_buttons; + return 0; +} + +//--------------------------------------------------------------------------- +// wxJoystick::IsOk +// +// Returns whether the joystick initialized successfully - in this case +// if the native implementation doesn't exist (in constructor) +//--------------------------------------------------------------------------- +bool wxJoystick::IsOk() const +{ + return m_hid != NULL; +} + +//--------------------------------------------------------------------------- +// wxJoystick::Get[XXX](Id/Name) +// +// Simple accessors to the native HID implementation +//--------------------------------------------------------------------------- +int wxJoystick::GetManufacturerId() const +{ + return m_hid->m_nManufacturerId; +} + +int wxJoystick::GetProductId() const +{ + return m_hid->m_nProductId; +} + +wxString wxJoystick::GetProductName() const +{ + return m_hid->m_szProductName; +} + +//--------------------------------------------------------------------------- +// wxJoystick::GetNumberButtons +// wxJoystick::GetNumberAxes +// +// Queries the joystick for an active number of buttons/axes. +// +// In the native HID implementation, the cookies: +// 0-40 are the buttons of the joystick +// 40-50 are the axes of the joystick +// +// These just query the native HID implementation as above. +//--------------------------------------------------------------------------- +int wxJoystick::GetNumberButtons() const +{ + int nCount = 0; + + for(int nIndex = 0; nIndex < 40; ++nIndex) + { + if(m_hid->HasElement(nIndex)) + ++nCount; + } + + return nCount; +} +int wxJoystick::GetNumberAxes() const +{ + int nCount = 0; + + for(int nIndex = 40; nIndex < 50; ++nIndex) + { + if(m_hid->HasElement(nIndex)) + ++nCount; + } + + return nCount; +} + +//--------------------------------------------------------------------------- +// wxJoystick::GetNumberJoysticks +// +// Gets the number of joysticks on the system. In HID that +// is all devices with the kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad +// identifiers. +//--------------------------------------------------------------------------- +int wxJoystick::GetNumberJoysticks() +{ + return + wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) + + wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); +} + +//--------------------------------------------------------------------------- +// wxJoystick::SetCapture +// +// Stops sending events from the thread to the window set in +// SetCapture and stops polling the joystick +//--------------------------------------------------------------------------- +bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq) +{ + if (m_thread) + { + m_thread->m_catchwin = win; + m_thread->m_polling = pollingFreq; + return true; + } + return false; +} + +//--------------------------------------------------------------------------- +// wxJoystick::ReleaseCapture +// +// Stops sending events from the thread to the window set in +// SetCapture and stops polling the joystick +//--------------------------------------------------------------------------- +bool wxJoystick::ReleaseCapture() +{ + if (m_thread) + { + m_thread->m_catchwin = NULL; + m_thread->m_polling = 0; + return true; + } + return false; +} + +//--------------------------------------------------------------------------- +// wxJoystick::Get[XXX] +// +// Gets the minimum and maximum values for each axis, returning 0 if the +// axis doesn't exist. +//--------------------------------------------------------------------------- +int wxJoystick::GetXMin() const +{ + return m_hid->m_nXMin; +} + +int wxJoystick::GetYMin() const +{ + return m_hid->m_nYMin; +} + +int wxJoystick::GetZMin() const +{ + return m_hid->m_nZMin; +} + +int wxJoystick::GetRudderMin() const +{ + return m_hid->m_nRudderMin; +} + +int wxJoystick::GetUMin() const +{ + return m_hid->m_nUMin; +} + +int wxJoystick::GetVMin() const +{ + return m_hid->m_nVMin; +} + +int wxJoystick::GetXMax() const +{ + return m_hid->m_nXMax; +} + +int wxJoystick::GetYMax() const +{ + return m_hid->m_nYMax; +} + +int wxJoystick::GetZMax() const +{ + return m_hid->m_nZMax; +} + +int wxJoystick::GetRudderMax() const +{ + return m_hid->m_nRudderMax; +} + +int wxJoystick::GetUMax() const +{ + return m_hid->m_nUMax; +} + +int wxJoystick::GetVMax() const +{ + return m_hid->m_nVMax; +} + +//--------------------------------------------------------------------------- +// wxJoystick::Get[XXX] +// +// Min/Max values for buttons, axes, etc.. Polling in this case is just +// what the linux port has. +//--------------------------------------------------------------------------- +int wxJoystick::GetMaxButtons() const +{ + return wxJS_MAX_BUTTONS; +} + +int wxJoystick::GetMaxAxes() const +{ + return wxJS_MAX_AXES; +} + +int wxJoystick::GetPollingMin() const +{ + return 10; +} + +int wxJoystick::GetPollingMax() const +{ + return 1000; +} + +//--------------------------------------------------------------------------- +// wxJoystick::Has[XXX] +// +// Just queries the native hid implementation if the cookie was found +// when enumerating the cookies of the joystick device +//--------------------------------------------------------------------------- +bool wxJoystick::HasZ() const +{ + return m_hid->HasElement(wxJS_AXIS_Z); +} + +bool wxJoystick::HasRudder() const +{ + return m_hid->HasElement(wxJS_AXIS_RUDDER); +} + +bool wxJoystick::HasU() const +{ + return m_hid->HasElement(wxJS_AXIS_U); +} + +bool wxJoystick::HasV() const +{ + return m_hid->HasElement(wxJS_AXIS_V); +} + +//--------------------------------------------------------------------------- +// UNSUPPORTED +//--------------------------------------------------------------------------- +int wxJoystick::GetPOVPosition() const +{ + return -1; +} + +int wxJoystick::GetPOVCTSPosition() const +{ + return -1; +} + +int wxJoystick::GetMovementThreshold() const +{ + return 0; +} + +void wxJoystick::SetMovementThreshold(int WXUNUSED(threshold)) +{ +} + +bool wxJoystick::HasPOV() const +{ + return false; +} + +bool wxJoystick::HasPOV4Dir() const +{ + return false; +} + +bool wxJoystick::HasPOVCTS() const +{ + return false; +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxHIDJoystick +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//--------------------------------------------------------------------------- +// wxHIDJoystick ctor +// +// Initializes the min/max members +//--------------------------------------------------------------------------- +wxHIDJoystick::wxHIDJoystick() : + m_nXMax(0), m_nYMax(0), m_nZMax(0), m_nRudderMax(0), m_nUMax(0), m_nVMax(0), + m_nXMin(0), m_nYMin(0), m_nZMin(0), m_nRudderMin(0), m_nUMin(0), m_nVMin(0) +{ +} + +//--------------------------------------------------------------------------- +// wxHIDJoystick dtor +// +// Nothing... +//--------------------------------------------------------------------------- +wxHIDJoystick::~wxHIDJoystick() +{ +} + +//--------------------------------------------------------------------------- +// wxHIDJoystick::Create +// +// Creates the native HID device (joysticks are of either +// kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad) +//--------------------------------------------------------------------------- +bool wxHIDJoystick::Create(int nWhich) +{ + int nJoysticks = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); + + if (nWhich <= nJoysticks) + return wxHIDDevice::Create(kHIDPage_GenericDesktop, + kHIDUsage_GD_Joystick, + nWhich); + else + nWhich -= nJoysticks; + + int nGamePads = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); + + if (nWhich <= nGamePads) + return wxHIDDevice::Create(kHIDPage_GenericDesktop, + kHIDUsage_GD_GamePad, + nWhich); + else + return false; +} + +//--------------------------------------------------------------------------- +// wxHIDJoystick::BuildCookies +// wxHIDJoystick::MakeCookies +// +// Sets up the cookies for the HID device (called from Create) - as +// mentioned 0-40 are the buttons and 40-50 are the axes. +// +// MakeCookies is just a recursive function for each array within +// BuildCookies. +//--------------------------------------------------------------------------- +void wxHIDJoystick::BuildCookies(CFArrayRef Array) +{ + InitCookies(50, true); + + // + // I wasted two hours of my life on this line :( + // accidently removed it during some source cleaning... + // + MakeCookies(Array); + + //paranoid debugging stuff +#if 0 + for(int i = 0; i < 50; ++i) + wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]); +#endif +}//end buildcookies + +void wxHIDJoystick::MakeCookies(CFArrayRef Array) +{ + int i, nUsage, nPage; + + for (i = 0; i < CFArrayGetCount(Array); ++i) + { + const void* ref = CFDictionaryGetValue( + (CFDictionaryRef)CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementKey) + ); + + if (ref != NULL) + { + MakeCookies((CFArrayRef) ref); + } + else + { + CFNumberGetValue( + (CFNumberRef) + CFDictionaryGetValue( + (CFDictionaryRef) CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementUsageKey) + ), + kCFNumberIntType, + &nUsage ); + + CFNumberGetValue( + (CFNumberRef) + CFDictionaryGetValue( + (CFDictionaryRef) CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementUsagePageKey) + ), + kCFNumberIntType, + &nPage ); + +#if 0 + wxLogSysError(wxT("[%i][%i]"), nUsage, nPage); +#endif + if (nPage == kHIDPage_Button && nUsage <= 40) + AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), nUsage-1 ); + else if (nPage == kHIDPage_GenericDesktop) + { + //axis... + switch(nUsage) + { + case kHIDUsage_GD_X: + AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_X); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMaxKey), + &m_nXMax); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMinKey), + &m_nXMin); + break; + case kHIDUsage_GD_Y: + AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_Y); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMaxKey), + &m_nYMax); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMinKey), + &m_nYMin); + break; + case kHIDUsage_GD_Z: + AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_Z); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMaxKey), + &m_nZMax); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMinKey), + &m_nZMin); + break; + default: + break; + } + } + else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder) + { + //rudder... + AddCookieInQueue(CFArrayGetValueAtIndex(Array, i), wxJS_AXIS_RUDDER ); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMaxKey), + &m_nRudderMax); + wxGetIntFromCFDictionary(CFArrayGetValueAtIndex(Array, i), + CFSTR(kIOHIDElementMinKey), + &m_nRudderMin); + } + } + } +} + +//--------------------------------------------------------------------------- +// wxHIDJoystick::Get[XXX] +// +// Simple accessors so that the HID callback and the thread procedure +// can access members from wxHIDDevice (our parent here). +//--------------------------------------------------------------------------- +IOHIDElementCookie* wxHIDJoystick::GetCookies() +{ return m_pCookies; } +IOHIDQueueInterface** wxHIDJoystick::GetQueue() +{ return m_ppQueue; } + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxJoystickThread +// +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//--------------------------------------------------------------------------- +// wxJoystickThread Constructor +// +// Just initializes members +//--------------------------------------------------------------------------- +wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick) + : m_hid(hid), + m_joystick(joystick), + m_lastposition(127,127), + m_buttons(0), + m_catchwin(NULL), + m_polling(0) +{ + memset(m_axe, 0, sizeof(int) * wxJS_MAX_AXES); +} + +//--------------------------------------------------------------------------- +// wxJoystickThread::Entry +// +// Thread procedure +// +// Runs a CFRunLoop for polling. Basically, it sets the HID queue to +// call wxJoystickThread::HIDCallback in the context of this thread +// when something changes on the device. It polls as long as the user +// wants, or a certain amount if the user wants to "block". Note that +// we don't actually block here since this is in a secondary thread. +//--------------------------------------------------------------------------- +void* wxJoystickThread::Entry() +{ + CFRunLoopSourceRef pRLSource = NULL; + + if ((*m_hid->GetQueue())->createAsyncEventSource( + m_hid->GetQueue(), &pRLSource) != kIOReturnSuccess ) + { + wxLogSysError(wxT("Couldn't create async event source")); + return NULL; + } + + wxASSERT(pRLSource != NULL); + + //attach runloop source to main run loop in thread + CFRunLoopRef pRL = CFRunLoopGetCurrent(); + CFRunLoopAddSource(pRL, pRLSource, kCFRunLoopDefaultMode); + wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) ); + + + if( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(), + wxJoystickThread::HIDCallback, this, this) != kIOReturnSuccess ) + { + wxLogSysError(wxT("Could not set event callout for queue")); + return NULL; + } + + if( (*m_hid->GetQueue())->start(m_hid->GetQueue()) != kIOReturnSuccess ) + { + wxLogSysError(wxT("Could not start queue")); + return NULL; + } + + double dTime; + + while(true) + { + if (TestDestroy()) + break; + + if (m_polling) + dTime = 0.0001 * m_polling; + else + dTime = 0.0001 * 10; // check at least every 10 msec in "blocking" case + + //true just "handles and returns" - false forces it to stay the time + //amount +#if 1 + CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true); +#else + IOReturn ret = NULL; + HIDCallback(this, ret, this, this); + Sleep(3000); +#endif + } + + wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) ); + + CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode); + CFRelease(pRLSource); + + return NULL; +} + +//--------------------------------------------------------------------------- +// wxJoystickThread::HIDCallback (static) +// +// Callback for the native HID device when it recieves input. +// +// This is where the REAL dirty work gets done. +// +// 1) Loops through each event the queue has recieved +// 2) First, checks if the thread that is running the loop for +// the polling has ended - if so it breaks out +// 3) Next, it checks if there was an error getting this event from +// the HID queue, if there was, it logs an error and returns +// 4) Now it does the real dirty work by getting the button states +// from cookies 0-40 and axes positions/states from cookies 40-50 +// in the native HID device by quering cookie values. +// 5) Sends the event to the polling window (if any) +// 6) Gets the next event and goes back to (1) +//--------------------------------------------------------------------------- +/*static*/ void wxJoystickThread::HIDCallback(void* WXUNUSED(target), + IOReturn WXUNUSED(res), + void* context, + void* WXUNUSED(sender)) +{ + IOHIDEventStruct hidevent; + AbsoluteTime bogustime = {0,0}; + IOReturn ret; + wxJoystickThread* pThis = (wxJoystickThread*) context; + wxHIDJoystick* m_hid = pThis->m_hid; + + //Get the "first" event from the queue + //bogustime tells it we don't care at what time to start + //where it gets the next from + ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), + &hidevent, bogustime, 0); + + while (ret != kIOReturnUnderrun) + { + if (pThis->TestDestroy()) + break; + + if(ret != kIOReturnSuccess) + { + wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret)); + return; + } + + wxJoystickEvent wxevent; + + //Find the cookie that changed + int nIndex = 0; + IOHIDElementCookie* pCookies = m_hid->GetCookies(); + while(nIndex < 50) + { + if(hidevent.elementCookie == pCookies[nIndex]) + break; + + ++nIndex; + } + + //debugging stuff +#if 0 + if(nIndex == 50) + { + wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error"))); + break; + } +#endif + + //is the cookie a button? + if (nIndex < 40) + { + if (hidevent.value) + { + pThis->m_buttons |= (1 << nIndex); + wxevent.SetEventType(wxEVT_JOY_BUTTON_DOWN); + } + else + { + pThis->m_buttons &= ~(1 << nIndex); + wxevent.SetEventType(wxEVT_JOY_BUTTON_UP); + } + + wxevent.SetButtonChange(nIndex+1); + } + else if (nIndex == wxJS_AXIS_X) + { + pThis->m_lastposition.x = hidevent.value; + wxevent.SetEventType(wxEVT_JOY_MOVE); + pThis->m_axe[0] = hidevent.value; + } + else if (nIndex == wxJS_AXIS_Y) + { + pThis->m_lastposition.y = hidevent.value; + wxevent.SetEventType(wxEVT_JOY_MOVE); + pThis->m_axe[1] = hidevent.value; + } + else if (nIndex == wxJS_AXIS_Z) + { + wxevent.SetEventType(wxEVT_JOY_ZMOVE); + pThis->m_axe[2] = hidevent.value; + } + else + wxevent.SetEventType(wxEVT_JOY_MOVE); + + Nanoseconds timestamp = AbsoluteToNanoseconds(hidevent.timestamp); + + wxULongLong llTime(timestamp.hi, timestamp.lo); + + llTime /= 1000000; + + wxevent.SetTimestamp(llTime.GetValue()); + wxevent.SetJoystick(pThis->m_joystick); + wxevent.SetButtonState(pThis->m_buttons); + wxevent.SetPosition(pThis->m_lastposition); + wxevent.SetZPosition(pThis->m_axe[2]); + wxevent.SetEventObject(pThis->m_catchwin); + + if (pThis->m_catchwin) + pThis->m_catchwin->AddPendingEvent(wxevent); + + ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), + &hidevent, bogustime, 0); + } +} + +#endif // wxUSE_JOYSTICK diff --git a/src/osx/core/stdpaths_cf.cpp b/src/osx/core/stdpaths_cf.cpp new file mode 100644 index 0000000000..69e524f1fc --- /dev/null +++ b/src/osx/core/stdpaths_cf.cpp @@ -0,0 +1,217 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: mac/corefoundation/stdpaths.cpp +// Purpose: wxStandardPaths implementation for CoreFoundation systems +// Author: David Elliott +// Modified by: +// Created: 2004-10-27 +// RCS-ID: $Id$ +// Copyright: (c) 2004 David Elliott +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "wx/wxprec.h" + +#if wxUSE_STDPATHS + +#ifndef WX_PRECOMP + #include "wx/intl.h" +#endif //ndef WX_PRECOMP + +#include "wx/stdpaths.h" +#include "wx/filename.h" +#ifdef __WXMAC__ +#include "wx/mac/private.h" +#endif +#include "wx/mac/corefoundation/cfstring.h" + +#include +#include + +#define kDefaultPathStyle kCFURLPOSIXPathStyle + +// ============================================================================ +// implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// wxStandardPathsCF ctors/dtor +// ---------------------------------------------------------------------------- + +wxStandardPathsCF::wxStandardPathsCF() + : m_bundle(CFBundleGetMainBundle()) +{ + CFRetain(m_bundle); + UseAppInfo(AppInfo_AppName | AppInfo_VendorName); +} + +wxStandardPathsCF::wxStandardPathsCF(wxCFBundleRef bundle) + : m_bundle(bundle) +{ + CFRetain(m_bundle); + UseAppInfo(AppInfo_AppName | AppInfo_VendorName); +} + +wxStandardPathsCF::~wxStandardPathsCF() +{ + CFRelease(m_bundle); +} + +// ---------------------------------------------------------------------------- +// wxStandardPathsCF Mac-specific methods +// ---------------------------------------------------------------------------- + +void wxStandardPathsCF::SetBundle(wxCFBundleRef bundle) +{ + CFRetain(bundle); + CFRelease(m_bundle); + m_bundle = bundle; +} + +// ---------------------------------------------------------------------------- +// generic functions in terms of which the other ones are implemented +// ---------------------------------------------------------------------------- + +static wxString BundleRelativeURLToPath(CFURLRef relativeURL) +{ + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(relativeURL); + wxCHECK_MSG(absoluteURL, wxEmptyString, wxT("Failed to resolve relative URL to absolute URL")); + CFStringRef cfStrPath = CFURLCopyFileSystemPath(absoluteURL,kDefaultPathStyle); + CFRelease(absoluteURL); + return wxCFStringRef(cfStrPath).AsString(wxLocale::GetSystemEncoding()); +} + +wxString wxStandardPathsCF::GetFromFunc(wxCFURLRef (*func)(wxCFBundleRef)) const +{ + wxCHECK_MSG(m_bundle, wxEmptyString, + wxT("wxStandardPaths for CoreFoundation only works with bundled apps")); + CFURLRef relativeURL = (*func)(m_bundle); + wxCHECK_MSG(relativeURL, wxEmptyString, wxT("Couldn't get URL")); + wxString ret(BundleRelativeURLToPath(relativeURL)); + CFRelease(relativeURL); + return ret; +} + +wxString wxStandardPathsCF::GetDocumentsDir() const +{ +#if defined( __WXMAC__ ) && !defined(__WXOSX_IPHONE__) + return wxMacFindFolderNoSeparator + ( + kUserDomain, + kDocumentsFolderType, + kCreateFolder + ); +#else + return wxFileName::GetHomeDir() + wxT("/Documents"); +#endif +} + +// ---------------------------------------------------------------------------- +// wxStandardPathsCF public API +// ---------------------------------------------------------------------------- + +wxString wxStandardPathsCF::GetConfigDir() const +{ +#if defined( __WXMAC__ ) && !defined(__WXOSX_IPHONE__) + return wxMacFindFolder((short)kLocalDomain, kPreferencesFolderType, kCreateFolder); +#else + return wxT("/Library/Preferences"); +#endif +} + +wxString wxStandardPathsCF::GetUserConfigDir() const +{ +#if defined( __WXMAC__ ) && !defined(__WXOSX_IPHONE__) + return wxMacFindFolder((short)kUserDomain, kPreferencesFolderType, kCreateFolder); +#else + return wxFileName::GetHomeDir() + wxT("/Library/Preferences"); +#endif +} + +wxString wxStandardPathsCF::GetDataDir() const +{ + return GetFromFunc(CFBundleCopySharedSupportURL); +} + +wxString wxStandardPathsCF::GetExecutablePath() const +{ +#ifdef __WXMAC__ +#if 1 + return GetFromFunc(CFBundleCopyBundleURL); +#else + // TODO remove if cf implementation ok + ProcessInfoRec processinfo; + ProcessSerialNumber procno ; +#ifdef __LP64__ + FSRef fsRef; +#else + FSSpec fsSpec; +#endif + + procno.highLongOfPSN = 0 ; + procno.lowLongOfPSN = kCurrentProcess ; + processinfo.processInfoLength = sizeof(ProcessInfoRec); + processinfo.processName = NULL; +#ifdef __LP64__ + processinfo.processAppRef = &fsRef; +#else + processinfo.processAppSpec = &fsSpec; +#endif + + GetProcessInformation( &procno , &processinfo ) ; +#ifdef __LP64__ + return wxMacFSRefToPath(&fsRef); +#else + return wxMacFSSpec2MacFilename(&fsSpec); +#endif +#endif + +#else + return wxStandardPathsBase::GetExecutablePath(); +#endif +} + +wxString wxStandardPathsCF::GetLocalDataDir() const +{ +#if defined( __WXMAC__ ) && !defined(__WXOSX_IPHONE__) + return AppendAppInfo(wxMacFindFolder((short)kLocalDomain, kApplicationSupportFolderType, kCreateFolder)); +#else + return AppendAppInfo(wxT("/Library/Application Support")); +#endif +} + +wxString wxStandardPathsCF::GetUserDataDir() const +{ +#if defined( __WXMAC__ ) && !defined(__WXOSX_IPHONE__) + return AppendAppInfo(wxMacFindFolder((short)kUserDomain, kApplicationSupportFolderType, kCreateFolder)); +#else + return AppendAppInfo(wxFileName::GetHomeDir() + _T("/Library/Application Support")); +#endif +} + +wxString wxStandardPathsCF::GetPluginsDir() const +{ + return GetFromFunc(CFBundleCopyBuiltInPlugInsURL); +} + +wxString wxStandardPathsCF::GetResourcesDir() const +{ + return GetFromFunc(CFBundleCopyResourcesDirectoryURL); +} + +wxString +wxStandardPathsCF::GetLocalizedResourcesDir(const wxString& lang, + ResourceCat category) const +{ + return wxStandardPathsBase:: + GetLocalizedResourcesDir(lang, category) + _T(".lproj"); +} + +#endif // wxUSE_STDPATHS diff --git a/src/osx/core/strconv_cf.cpp b/src/osx/core/strconv_cf.cpp new file mode 100644 index 0000000000..e68d8e4ef6 --- /dev/null +++ b/src/osx/core/strconv_cf.cpp @@ -0,0 +1,228 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/corefoundation/strconv.cpp +// Purpose: Unicode conversion classes +// Author: David Elliott +// Modified by: +// Created: 2007-07-06 +// RCS-ID: $Id$ +// Copyright: (c) 2007 David Elliott +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/string.h" +#endif + +#include "wx/strconv.h" +#include "wx/fontmap.h" + +#ifdef __DARWIN__ + +#include "wx/mac/corefoundation/private/strconv_cf.h" +#include "wx/mac/corefoundation/cfref.h" + + +// ============================================================================ +// CoreFoundation conversion classes +// ============================================================================ + +/* Provide factory functions for unit tests. Not in any header. Do not + * assume ABI compatibility even within a given wxWidgets release. + */ + +WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_cf( const char* name) +{ + wxMBConv_cf *result = new wxMBConv_cf(name); + if(!result->IsOk()) + { + delete result; + return NULL; + } + else + return result; +} + +WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_cf(wxFontEncoding encoding) +{ + wxMBConv_cf *result = new wxMBConv_cf(encoding); + if(!result->IsOk()) + { + delete result; + return NULL; + } + else + return result; +} + +// Provide a constant for the wchat_t encoding used by the host platform. +#ifdef WORDS_BIGENDIAN + static const CFStringEncoding wxCFStringEncodingWcharT = kCFStringEncodingUTF32BE; +#else + static const CFStringEncoding wxCFStringEncodingWcharT = kCFStringEncodingUTF32LE; +#endif + + size_t wxMBConv_cf::ToWChar(wchar_t * dst, size_t dstSize, const char * src, size_t srcSize) const + { + wxCHECK(src, wxCONV_FAILED); + + /* NOTE: This is wrong if the source encoding has an element size + * other than char (e.g. it's kCFStringEncodingUnicode) + * If the user specifies it, it's presumably right though. + * Right now we don't support UTF-16 in anyway since wx can do a better job. + */ + if(srcSize == wxNO_LEN) + srcSize = strlen(src) + 1; + + // First create the temporary CFString + wxCFRef theString( CFStringCreateWithBytes ( + NULL, //the allocator + (const UInt8*)src, + srcSize, + m_encoding, + false //no BOM/external representation + )); + + wxCHECK(theString != NULL, wxCONV_FAILED); + + /* NOTE: The string content includes the NULL element if the source string did + * That means we have to do nothing special because the destination will have + * the NULL element iff the source did and the NULL element will be included + * in the count iff it was included in the source count. + */ + + +/* If we're compiling against Tiger headers we can support direct conversion + * to UTF32. If we are then run against a pre-Tiger system, the encoding + * won't be available so we'll defer to the string->UTF-16->UTF-32 conversion. + */ + if(CFStringIsEncodingAvailable(wxCFStringEncodingWcharT)) + { + CFRange fullStringRange = CFRangeMake(0, CFStringGetLength(theString)); + CFIndex usedBufLen; + + CFIndex charsConverted = CFStringGetBytes( + theString, + fullStringRange, + wxCFStringEncodingWcharT, + 0, + false, + // if dstSize is 0 then pass NULL to get required length in usedBufLen + dstSize != 0?(UInt8*)dst:NULL, + dstSize * sizeof(wchar_t), + &usedBufLen); + + // charsConverted is > 0 iff conversion succeeded + if(charsConverted <= 0) + return wxCONV_FAILED; + + /* usedBufLen is the number of bytes written, so we divide by + * sizeof(wchar_t) to get the number of elements written. + */ + wxASSERT( (usedBufLen % sizeof(wchar_t)) == 0 ); + + // CFStringGetBytes does exactly the right thing when buffer + // pointer is NULL and returns the number of bytes required + return usedBufLen / sizeof(wchar_t); + } + else + { + // NOTE: Includes NULL iff source did + /* NOTE: This is an approximation. The eventual UTF-32 will + * possibly have less elements but certainly not more. + */ + size_t returnSize = CFStringGetLength(theString); + + if (dstSize == 0 || dst == NULL) + { + return returnSize; + } + + // Convert the entire string.. too hard to figure out how many UTF-16 we'd need + // for an undersized UTF-32 destination buffer. + CFRange fullStringRange = CFRangeMake(0, CFStringGetLength(theString)); + UniChar *szUniCharBuffer = new UniChar[fullStringRange.length]; + + CFStringGetCharacters(theString, fullStringRange, szUniCharBuffer); + + wxMBConvUTF16 converter; + returnSize = converter.ToWChar( dst, dstSize, (const char*)szUniCharBuffer, fullStringRange.length ); + delete [] szUniCharBuffer; + + return returnSize; + } + // NOTREACHED + } + + size_t wxMBConv_cf::FromWChar(char *dst, size_t dstSize, const wchar_t *src, size_t srcSize) const + { + wxCHECK(src, wxCONV_FAILED); + + if(srcSize == wxNO_LEN) + srcSize = wxStrlen(src) + 1; + + // Temporary CFString + wxCFRef theString; + +/* If we're compiling against Tiger headers we can support direct conversion + * from UTF32. If we are then run against a pre-Tiger system, the encoding + * won't be available so we'll defer to the UTF-32->UTF-16->string conversion. + */ + if(CFStringIsEncodingAvailable(wxCFStringEncodingWcharT)) + { + theString = wxCFRef(CFStringCreateWithBytes( + kCFAllocatorDefault, + (UInt8*)src, + srcSize * sizeof(wchar_t), + wxCFStringEncodingWcharT, + false)); + } + else + { + wxMBConvUTF16 converter; + size_t cbUniBuffer = converter.FromWChar( NULL, 0, src, srcSize ); + wxASSERT(cbUniBuffer % sizeof(UniChar)); + + // Will be free'd by kCFAllocatorMalloc when CFString is released + UniChar *tmpUniBuffer = (UniChar*)malloc(cbUniBuffer); + + cbUniBuffer = converter.FromWChar( (char*) tmpUniBuffer, cbUniBuffer, src, srcSize ); + wxASSERT(cbUniBuffer % sizeof(UniChar)); + + theString = wxCFRef(CFStringCreateWithCharactersNoCopy( + kCFAllocatorDefault, + tmpUniBuffer, + cbUniBuffer / sizeof(UniChar), + kCFAllocatorMalloc + )); + + } + + wxCHECK(theString != NULL, wxCONV_FAILED); + + CFIndex usedBufLen; + + CFIndex charsConverted = CFStringGetBytes( + theString, + CFRangeMake(0, CFStringGetLength(theString)), + m_encoding, + 0, // FAIL on unconvertible characters + false, // not an external representation + // if dstSize is 0 then pass NULL to get required length in usedBufLen + (dstSize != 0)?(UInt8*)dst:NULL, + dstSize, + &usedBufLen + ); + + // charsConverted is > 0 iff conversion succeeded + if(charsConverted <= 0) + return wxCONV_FAILED; + + return usedBufLen; + } + +#endif // __DARWIN__ + + diff --git a/src/osx/core/utilsexc_base.cpp b/src/osx/core/utilsexc_base.cpp new file mode 100644 index 0000000000..377b2464be --- /dev/null +++ b/src/osx/core/utilsexc_base.cpp @@ -0,0 +1,229 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: mac/corefoundation/utilsexc_base.cpp +// Purpose: wxMacLaunch +// Author: Ryan Norton +// Modified by: +// Created: 2005-06-21 +// RCS-ID: $Id$ +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence +// Notes: Source was originally in utilsexc_cf.cpp,1.6 then moved +// to totally unrelated hid.cpp,1.8. +///////////////////////////////////////////////////////////////////////////// + +//=========================================================================== +// DECLARATIONS +//=========================================================================== + +//--------------------------------------------------------------------------- +// Pre-compiled header stuff +//--------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +// WX includes +#ifndef WX_PRECOMP + #include "wx/string.h" + #include "wx/log.h" + #include "wx/intl.h" + #include "wx/utils.h" + #include "wx/wxcrt.h" +#endif // WX_PRECOMP + +// Mac Includes +#include +#ifndef __WXOSX_IPHONE__ +#include +#endif + +// More WX Includes +#include "wx/filename.h" +#include "wx/mac/corefoundation/cfstring.h" + +// Default path style +#define kDefaultPathStyle kCFURLPOSIXPathStyle + +//=========================================================================== +// IMPLEMENTATION +//=========================================================================== + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// wxMacLaunch +// +// argv is the command line split up, with the application path first +// flags are the flags from wxExecute +// process is the process passed from wxExecute for pipe streams etc. +// returns -1 on error for wxEXEC_SYNC and 0 on error for wxEXEC_ASYNC +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +bool wxMacLaunch(char **argv) +{ + // Obtains the number of arguments for determining the size of + // the CFArray used to hold them + CFIndex cfiCount = 0; + for(char** argvcopy = argv; *argvcopy != NULL ; ++argvcopy) + { + ++cfiCount; + } + + // If there is not a single argument then there is no application + // to launch + if(cfiCount == 0) + { + wxLogDebug(wxT("wxMacLaunch No file to launch!")); + return false ; + } + + // Path to bundle + wxString path = *argv++; + + // Create a CFURL for the application path + // Created this way because we are opening a bundle which is a directory + CFURLRef cfurlApp = + CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, + wxCFStringRef(path), + kDefaultPathStyle, + true); //false == not a directory + + // Check for error from the CFURL + if(!cfurlApp) + { + wxLogDebug(wxT("wxMacLaunch Can't open path: %s"), path.c_str()); + return false ; + } + + // Create a CFBundle from the CFURL created earlier + CFBundleRef cfbApp = CFBundleCreate(kCFAllocatorDefault, cfurlApp); + + // Check to see if CFBundleCreate returned an error, + // and if it did this was an invalid bundle or not a bundle + // at all (maybe a simple directory etc.) + if(!cfbApp) + { + wxLogDebug(wxT("wxMacLaunch Bad bundle: %s"), path.c_str()); + CFRelease(cfurlApp); + return false ; + } + + // Get the bundle type and make sure its an 'APPL' bundle + // Otherwise we're dealing with something else here... + UInt32 dwBundleType, dwBundleCreator; + CFBundleGetPackageInfo(cfbApp, &dwBundleType, &dwBundleCreator); + if(dwBundleType != 'APPL') + { + wxLogDebug(wxT("wxMacLaunch Not an APPL bundle: %s"), path.c_str()); + CFRelease(cfbApp); + CFRelease(cfurlApp); + return false ; + } + + // Create a CFArray for dealing with the command line + // arguments to the bundle + CFMutableArrayRef cfaFiles = CFArrayCreateMutable(kCFAllocatorDefault, + cfiCount-1, &kCFTypeArrayCallBacks); + if(!cfaFiles) //This should never happen + { + wxLogDebug(wxT("wxMacLaunch Could not create CFMutableArray")); + CFRelease(cfbApp); + CFRelease(cfurlApp); + return false ; + } + + // Loop through command line arguments to the bundle, + // turn them into CFURLs and then put them in cfaFiles + // For use to launch services call + for( ; *argv != NULL ; ++argv) + { + // Check for '<' as this will ring true for + // CFURLCreateWithString but is generally not considered + // typical on mac but is usually passed here from wxExecute + if (wxStrcmp(*argv, wxT("<")) == 0) + continue; + + + CFURLRef cfurlCurrentFile; // CFURL to hold file path + wxFileName argfn(*argv); // Filename for path + + if(argfn.DirExists()) + { + // First, try creating as a directory + cfurlCurrentFile = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, + wxCFStringRef(*argv), + kDefaultPathStyle, + true); //true == directory + } + else if(argfn.FileExists()) + { + // And if it isn't a directory try creating it + // as a regular file + cfurlCurrentFile = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, + wxCFStringRef(*argv), + kDefaultPathStyle, + false); //false == regular file + } + else + { + // Argument did not refer to + // an entry in the local filesystem, + // so try creating it through CFURLCreateWithString + cfurlCurrentFile = CFURLCreateWithString( + kCFAllocatorDefault, + wxCFStringRef(*argv), + NULL); + } + + // Continue in the loop if the CFURL could not be created + if(!cfurlCurrentFile) + { + wxLogDebug( + wxT("wxMacLaunch Could not create CFURL for argument:%s"), + *argv); + continue; + } + + // Add the valid CFURL to the argument array and then + // release it as the CFArray adds a ref count to it + CFArrayAppendValue( + cfaFiles, + cfurlCurrentFile + ); + CFRelease(cfurlCurrentFile); // array has retained it + } + + // Create a LSLaunchURLSpec for use with LSOpenFromURLSpec + // Note that there are several flag options (launchFlags) such + // as kLSLaunchDontSwitch etc. and maybe we could be more + // picky about the flags we choose + LSLaunchURLSpec launchspec; + launchspec.appURL = cfurlApp; + launchspec.itemURLs = cfaFiles; + launchspec.passThruParams = NULL; //AEDesc* + launchspec.launchFlags = kLSLaunchDefaults; + launchspec.asyncRefCon = NULL; + + // Finally, call LSOpenFromURL spec with our arguments + // 2nd parameter is a pointer to a CFURL that gets + // the actual path launched by the function + OSStatus status = LSOpenFromURLSpec(&launchspec, NULL); + + // Cleanup corefoundation references + CFRelease(cfbApp); + CFRelease(cfurlApp); + CFRelease(cfaFiles); + + // Check for error from LSOpenFromURLSpec + if(status != noErr) + { + wxLogDebug(wxT("wxMacLaunch LSOpenFromURLSpec Error: %d"), + (int)status); + return false ; + } + + // No error from LSOpenFromURLSpec, so app was launched + return true ; +} + diff --git a/src/osx/core/utilsexc_cf.cpp b/src/osx/core/utilsexc_cf.cpp new file mode 100644 index 0000000000..2d95454fe9 --- /dev/null +++ b/src/osx/core/utilsexc_cf.cpp @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/mac/corefoundation/utilsexec_cf.cpp +// Purpose: Execution-related utilities for Darwin +// Author: David Elliott, Ryan Norton (wxMacExecute) +// Modified by: Stefan Csomor (added necessary wxT for unicode builds) +// Created: 2004-11-04 +// RCS-ID: $Id$ +// Copyright: (c) David Elliott, Ryan Norton +// Licence: wxWindows licence +// Notes: This code comes from src/mac/carbon/utilsexc.cpp,1.11 +///////////////////////////////////////////////////////////////////////////// + +#include "wx/wxprec.h" +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/utils.h" +#endif //ndef WX_PRECOMP +#include "wx/unix/execute.h" +#include "wx/stdpaths.h" +#include "wx/app.h" +#include "wx/apptrait.h" +#include "wx/thread.h" +#include "wx/process.h" + +#include + +#include + +/*! + Called due to source signal detected by the CFRunLoop. + This is nearly identical to the wxGTK equivalent. + */ +extern "C" void WXCF_EndProcessDetector(CFSocketRef s, + CFSocketCallBackType WXUNUSED(callbackType), + CFDataRef WXUNUSED(address), + void const *WXUNUSED(data), + void *info) +{ + /* + Either our pipe was closed or the process ended successfully. Either way, + we're done. It's not if waitpid is going to magically succeed when + we get fired again. CFSocketInvalidate closes the fd for us and also + invalidates the run loop source for us which should cause it to + release the CFSocket (thus causing it to be deallocated) and remove + itself from the runloop which should release it and cause it to also + be deallocated. Of course, it's possible the RunLoop hangs onto + one or both of them by retaining/releasing them within its stack + frame. However, that shouldn't be depended on. Assume that s is + deallocated due to the following call. + */ + CFSocketInvalidate(s); + + // Now tell wx that the process has ended. + wxHandleProcessTermination(static_cast(info)); +} + +/*! + Implements the GUI-specific AddProcessCallback() for both wxMac and + wxCocoa using the CFSocket/CFRunLoop API which is available to both. + Takes advantage of the fact that sockets on UNIX are just regular + file descriptors and thus even a non-socket file descriptor can + apparently be used with CFSocket so long as you only tell CFSocket + to do things with it that would be valid for a non-socket fd. + */ +int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd) +{ + static int s_last_tag = 0; + CFSocketContext context = + { 0 + , static_cast(proc_data) + , NULL + , NULL + , NULL + }; + CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context); + if(cfSocket == NULL) + { + wxLogError(wxT("Failed to create socket for end process detection")); + return 0; + } + CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0); + if(runLoopSource == NULL) + { + wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection")); + // closes the fd.. we can't really stop it, nor do we necessarily want to. + CFSocketInvalidate(cfSocket); + CFRelease(cfSocket); + return 0; + } + // Now that the run loop source has the socket retained and we no longer + // need to refer to it within this method, we can release it. + CFRelease(cfSocket); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); + // Now that the run loop has the source retained we can release it. + CFRelease(runLoopSource); + + /* + Feed wx some bullshit.. we don't use it since CFSocket helpfully passes + itself into our callback and that's enough to be able to + CFSocketInvalidate it which is all we need to do to get everything we + just created to be deallocated. + */ + return ++s_last_tag; +} + +///////////////////////////////////////////////////////////////////////////// + +// NOTE: This doesn't really belong here but this was a handy file to +// put it in because it's already compiled for wxCocoa and wxMac GUI lib. +#if wxUSE_STDPATHS +static wxStandardPathsCF gs_stdPaths; +wxStandardPathsBase& wxGUIAppTraits::GetStandardPaths() +{ + return gs_stdPaths; +} +#endif + diff --git a/src/osx/wxmac.icns b/src/osx/wxmac.icns new file mode 100644 index 0000000000000000000000000000000000000000..5ec807b5d66d0e6f9b78cbee335154826d2e64e0 GIT binary patch literal 45458 zcmeHw1wd3;`}bjnQX0j?0t1moR4_nPL=i!y6vRLgq*QEO7(fXHr35T&30sTzMCe?+w@08RLdcqc z(iq%m-Z-Gq_;yGLLxw?TL@*A75DzhhAbMl~++Isu_jm=)EsHhkl8=Dg_fsac%8qD!MoN=oWmKviAc;x3^_gQ}Fig&I`VZ{H>K_4M?iOtnx~zjf=* zBZ9y+f~dPQ6-u4Zw~j8n1C5Pf9R56vqR1%-+Up_0Vs1G6J9!87>P5a9LWPWcI;=b6jkP>(}Vx{*;T}>q#Vp1;l7-7(_dsiBRbWsK& zX=NojX^AxKVIGge;hu+qJ8|vTr@ubsgd>TZ_aOv&3>QUjLkRlqB{s7|5PiwKq(&H( zvaDl!9eBt8>9{B+k&kJ;Gxv1uVDM>SpJtAuc} z{&GmayW|DG{#+VSbw2;=E-2vLhYugFPd2^y>C>l=2PF`a?b=li?l;rJw{8v775dWA z#k5H?;a=9x-md^3xJvLN)9OcFhTsmQzvpr#xRc;#7*~P;KU{P8QR33MN{{GVbNC^+ z^vA>_`ZJ`;3zBKy-u{(ejkeCf+p(yDrlH5tbgIkS5{WuY*vTrH|lS>Kwjr0 zb~1rPtR)zRaQbB zY*+b`b>LSS@CkHRE%U^5s8aRHTBzTXhpGzjS5}q|wsBfo1RprK4pj{eO@a{Ai%Kmu zG>i%29DD$Q{A-`jQN0>XfJ1%qyQkXAqp(Xm7JPpvvu*gnvZM+ZR6zN*yN9G1PUr*V2VpkXsA1<-!D9}e^*R6W79H2sN3$nc{*`N z1S4>X`;7jvNXa_(%HgfS8amgNM4pts?K^i3W@Y0S%vTXMBO|1>SbXX1EMI7;*sF7;ZxV7{ZlqGnU5hNp0uZ)ki>L}=JdyQV2r!&A5@ zs_s?=CqLiAvS||4@FaR1eRk>mi6e(sX}VkM@aM4?e;!@;+-JeE+p%qidf#5%l*p;< zE1XJ)9XHOCi*VU&t9~>(HJ?42=F>K`GA$f0%Bhpb_h>$?D&(}92A1t#4~uis1XDxh zrpZ+yCwGKH&JMgd#~AAll@CGG6suBGJkuG8B z>#0m{G2L?gH0Dg#1wt=XdC4tJQ{JU}cQrK?#jY}v5(!9JOF?*aZPL3Li9Ru1g4!p3VZ5y<*PaI-3fVuubdc8VF~0 z&w;yJ2-nDC*N*Ir4BfD7uG6HU2=Qk&W@~C??d|)uwGY?tyReI~d8L!}WDC8X2nptP z)xt#!%wo$*6U-8MQGs4|lP%0f4OKy?X>KQumJ30B`>fAi>oV2c%tT+ipFBd%=T-&C z>V8LE#+i-O*B(5eJDl)Z&2HZTZU^j*41n0XyBrOnRue3zqGLO1I1sxj%EBp8zSR`V zDynu@QNYs|idxOFq`Z=>6b(+>?M<;Sd4iMtz7w3NNB;kKo0Pqu4=#R=a8MIbd>_1i zh0E(-Hod4%NLNx$Aq8SMe;U7PK zZPw6AzIeE{_VD8N>F_CqP)_^wJWS7TMvrLeA4$({Mh|J}Up|&!+(fS*jZpJ>BjwSm zYt*w<9orn(CpE(p0H4H#CQClt!gj^Q<#q$Pt{R@jN>eQ@UnQpT*QHzcmV|#q&D+BZ z5Dc1i)1ZCt)d9@-6;ZEr@>=3MUb`@qKuQ_mR|6!|XpSBs`Xf4>d{rV}2f!;=0>3_z zAnM^2QLh8)k<24Ry_%EP&@NL?CtsC_M{sQp*GlyOECU;cmx&MHqdE5RZx-<sj1Kk6Ag{mu3O&H1*+{;A2JDC+J`W&Y&A;olaRBkzoao?HiD(2K83G2ZQG(#Y zD>yg+Oe2_>NAN0cfpr83kYprw@&7`>pwoRv$JSl>Lbl((*#tjP9QQF^1f{jWeI!q| zmn~?i=^o%ixQN@nrj;Hy0F(f)TDK;krGB!#VvVO~EB^VI9q_9Ackcq=#S{wR_H~|~ zBpHA&j(`9S>wq1@UjTqrVgZ1}m`umY3TXD@Qq7eBHQ|wAGMy^R0qr8&J|V+>QmypcUGcp@N$+KRE(bPJ(t4 zI3x9Ae#^l&zj}K5C?m2N^RqDjWDN~XbLfh?ys#bn3C)6bjGvMEp?xx7a&6j;D0U5F zN)N>kei~y$4=_BkT?M0lxZw6;vYBe9TCqRIGb}MT$9AGzOtA{~!eNpFQ5+>wPnv72wnk zrJ7J#{?)f77{N_AEJjO3VzJFs06XBaP)KkUW3!^b)h2;p$G4cD&kb)K@jJBOh7fKC z`kV6uUMmJa=Kd~z(s_~@;M@-U$p|7lE8qrf`J*m{E;~D8c(`?;_%S=iii4eWb#-)f z1x3%LH9L_JMB&mDIP9!LV$TAESyxYR79%lg3{z*M3vp~vuS>CGJee$fk1(LkFX8DJ zGRM9^`9P`{FvB~=4-hx?m=8$vR?JUv13%W^#7?IR_+{Rb0)Sd?&1mAUZov;2H3@J7 zgl$CflPI@aRgh4BHKR#C=5EPPvMU;8{K@yGx|-xC-NC#VgvJ0msh^LbgUp!B=QV=z z5l|coX(zcUKpprYL4X3)F*2@ZYHGgxMa}c)H7{Ir1^ie!6b!&O@LP}x2&8Tb`?jdVY5)@k3zXwhvZ=i;$7LAhl!t>OyybTT^rDJ&L=PgrM=x9Z29H%Pq1@ zoth;eiGCLZ82Il~r;284YQ5vrT;!h^muTwC|Me?xFC2mC8czV2OP~m(uw=zDkA?28 z^Bf&!+1bxdManC@{R8}#FIx(5`aBm$J6p%SNX6YRx~P~{kbUOF@k9Gl5`tz=+ly2^ zLT^02QO+yMJAW$uNb2sC&65oGAk~Gbj~-VQW}nGCc5pACxsh9EjDlA8eRU7Y&OzHg zXp0C9+BhBaIwjWJVWk0IJZ{^tX2m2OXq~ma@Nag7=W^OM2pp|hJaf9W`J_cILCmdZBnq>4B31Lif{KG7 zpw(@<^^~!W$188$48KOZ$l1RGDQml*D@j?uV!4ymK9fT*Y4Z}Sr%--A#N?fj)r;e_H`tu2|p6EJC* zW{(X|`9uj$T?C=HRc&Udn~~m#VVVQg71ln5h|`zGcke&ffwTns9xy!7SJfUdY=~z6 zzCF81t$GTudSgA~1+AevpX7)n6l!dGsHn)xDRh;UM!wGgmjC+pMaWB9!*z%mfsSp2 z-0h25(P~JNhE!7@g#rByM3IDaaOYWe7^0oH@g;N+T9(! zynMYpmn?F3x7V30dVBQt^INfgZQu$&A1|*l<3;U|K1+SJmt4j-j;zxsCyx}hOZs?h zy!xcJoKu*4?qud99Z`FwkH?O?Pp=ha;Tz2%r+)2jnLhKk*F3tAm4RgZ~T z-Z_0-!Y#k`?ck?ozxrMhHDKO)W1*9 zZp!jqq$Ktu1vNEQRb@r_u3coL;Gy;)lI-3^R$7`t16-BdyL*H?n0|y^kh?WcBA=Ox z=tWsPUO1P|V;AM_Mf7u=n&**kX}?}5gquPBl`n9!s4vdi12Ic{v%XRQSIlQ{b-fU` zwYp*n+V13&SKA21Pd74yon4)r93AG&ws)}EiG`vFSsevxLe2h)s-(wx1M= zBQc zx6}$jO3ij#Y+U}mcZ^S=AG>{uTUJRfnGmF;BCA66oI$Tgpk6W z%N(pJP93eGy&$eDdIzfZGTnS`^Gpj;C){8L^;eYvUXlAywa3WyXV*_N8#B^CXE?d6 z6(oUIfv$LpAV&0nQi>MWT17qKf(; zO^t#5`t<0otk6Xo_N(X}DyOWZLf!)LE(=E@cd4|B66`%mS`_+=JJpMfICuF6MVv9X z{eQox%vijLUluO}?6iZ@XaAGe4gB)Dp~hcSti72E)vK*eT3ebCMO4e@Z-@V+2_9aPldZH9L_7TPco-0h z|6cwiia+V!%-?q4$jq9y?p2{npmF={eH;GS0)7aAW0aOGl7dF5Gpz(plX*P?a@%*gZ4ZR^*oM~+BIVk$ zv}}=`EWC#=T0DiHxYClPv-UqS0^X}JGB+j^yF;B~zW0?D5D*x;G<0F8YnW5$EZWRj zj&+c^vpSFwFu>o>*Jru67yRnUk|iFC7cE@iKHtrC-rTt^&Tvd|m}6aw2CiJS3Qi`z ze7>a=+XBosk78rYk#umdpXpeGv;$TI2Ke}{T)TG7>cCYiR|N3YdwMPt*H^6_T&qiilU2V4uz2#+$z!XKzMsF3Pwwq& zHy%E`bNvG86BGMH38e7_Qj!wmV`HOcSz1l3LI%DoJR@taRzG|C;CA&z=md7CrG?$e zSYSu=w&(y0OLOhZ$k1`Q&(h55%a8u`%e~qw6`UrgGSZG7#!l@{-bIN-MT7@gO&D(q zLW{k zu9=W}_#KE7LXxug()8NOrt_K@QR-KRu? zHg8XD2;CQogm1xCHi5|MRjX`9j@HLQ z?(R#^<`+JGcYHk%` zQpDTKYm&Ypo?Pp>F7C%qo_TP$`T{#U^8oG(-%2<*hDFw`B}JBdEnB)|qOO4!7IJo) ze>mgh{X5sni_WL*YZBQKN{OuT_jI2N*ufk}mw5}!N9YZ~LQW2@2aaXjy?v#$;LOq8 z@nl~j!(l3eNh`kYjs@3F4mfHO_`eUJ4vz${>58t_YiJP5~8n-=)6bahAX~VkJ ztCu^^vbO_~X<%lubAf&S_}(26*hY

rfIg)Hn2`BqB(w#WNW>!pm^3Cg|1*Z-sM&JRYWc}uYpVRShfKdyx z=~0DcCA{5SXt{7uSx*@l1Bm9aP%ct27u2pfej;Cz<2?hydeLNkfQ&TY!WXFz~ zlwQK-{rowVka~BM#i?LtqT$9yMkZcC+pk_`ojtNEV*Sbhf8XWai|waT6KFAE{5UgH z-N1TM0-0-JasVMs_p1AV;_`WeNX zdJuh3RKbl#GDXE@Y*umA&Dc9y_ezb2>)FqCW#u1B3|qO-R^YA~>8_!^p3Vp@-4n&! z29SOPhPQ(>uY|?B_vULnG3?36VLG;Ub6I&u6G8&!+q5#PGeTQysCH(P@HQlMj8n!c zd%lYp_KVSw5z}Wl7v&y~3-)uho-hFh7yKP*WM}|3hieTRs;SjNR1%g#@rxv4*w2Q8 zhg(l`WM&`Q8RX+KWh}_yp#@)c!Qil=gEa>ZX(kzkB#&@QSPznkVNXYC44pjHJ}>*g zj?LaqlZ5stxgh`u_1DN~au*~Ed7RSsAXz_gfaZ9sDaUeBqc<*dumC@e!B2s`K?4W$ z>pQT8B%NK(s@_cuUDkJ?!KCra&Zll$x5R!-3oo%0{0>dO^yU`nyo#cGAa}giK#j2z z%mPxjuUk0L(9l3%U<-Wgr{1S`FSXuSZV#dK_2c_DuN&UHdH3NnfoCWR(K!`Gk3h%o zWBc?QHrCW+${ZUrSOuVAI369+ZlB&gd#I`QOamP$gvyt9uU@`*@e=;Mcv=6Zjw#f# zhmbj}rrvj`$tWX+ejhzu9cq9zuMZJ`KbtAi&$#-E7G30}v za-ZJ)we`U~p46d325Sr&&|kf8pI$wCbnm93te}d8cM%d_-fSm@Ik|k{y#(#0yAq>k z|32#du|V%$J$tHwITdAPB}I8TIe^?0j)QC>{A$;$=$5kk2+cPkeH2u?ckiZ32`DIl zNY^g1vNF=rQj(GqU9o5aA@Q+3rln{qAy>I%n6eyZl$DWXC@|!dKu$&m#OO3c%0cM( z!_Jms2Z^q~=I!1d85Oe3dx27jbciG*nn0vL;SAV`BT!>(Oa9}8D7YW$ML;;7} z5DP4EZCDz(#hfT$iOMTvzS>Ek_wntqNC8XUZ(jhb|ME7WJtk>j%74-TF+qvX4T z$&y_4FF$+cAR2z3p`z#^n0v)3;bz7m zsqK0B5U<*oBbisoDa?)`>34wsC%c4~nSkhd`T4AU96ETF37?yP~8LNfEp88XJuy$# zll=fLUYUB~*6mw2F#rsRE?_qR02cxfnsdlN%(M0sPXLG4G4I`~xdtDb7#%L=awsqv zz~0k)^@fUf0H5N&%4Y?$uBQ6tjjJS_%_WiG+$<70K9Q^|=CRz$)!%k($^I4v~K0?`M@ec7; zzJ5!>uU@MC{D*Dn^gfxY)z_ZRPUZ{_8)D50vn?vKY9c4lK(ACPCj>G_cOJ}>pZ ze(bke;=LgGQt|D7{agp|G&7s(`0=AhAjkk{d(y5*J$3cJjt6Kh_Hy4<&bj&f!`s&` zaP#q~3UGM}{!Uof`!65`R*O8{;!Bt{&+gX(5TAqNA^?yJ(C%2kp(7{t6dyydTCl`* zCnxv%uXk@$78jnws5==6;iK+=hergNssW}T9=u@Xws>v~GwbTpx@rLPPg4N{s`~L0f7yID!LnRNdSFp0u_fruF3biMZ zbnkfp^i!xkfT`m&hYj66X8rs1+kx6aQw;1KW^6c=!n${*q~O%yq-coM2o$YZ?d^mevZYXV2qTz}?x)^+ z2WlU3F|eIIeeM2T%)3>^xhD=J@H| zS5SAEo1MNdcB>%luzKZ+MSS0&mn4io*38rhaKatv8{mz>G`p!QlVbAjTx6X;zB@W3 z=qEuO!X1x1KlnkR?W0HO18%tkwTti=m`=6wkB`c}UBNtiBq=J0ilYG7@7g>vF&_NF z$hK~;9^JmBY~+~nzB|Lu*OozqBq4kwM5J)EVY!P9=@r#Q5_BF3=(;wbq2E$wG|JE| zD(vjd(!9+5v7tD)MFlRXAPoeBFy1~I1N8uPYN>Ts`Ia&R69XH+^`~zX;}}ZtDjdyN zzK9>q;A7~dDiYEj(yg2Fx0LA_>rG$ecCdz*4I!1y0lq%op7U&`lA)t!Q2-Lj*B-8^ zs-g(W+JmMH^o(?^7dRzeDmZ&IDRP6a_fj`|D*9nz-UQGCmaBuIUwiVrS#f1&Qe!qR zhYpgF6&x?(t0EC*3Y{J_u&<&*7f{w75@s-5Z}>##nLA5Q?T_B%?P5KpX)y>Q31kNc zLquRezh3h4vO7_GaG8OYj+VKj&5qKO`?hUZ=4gd?4mnhyiHs4Dns96Zqy0VQy2`|g z#?%akju<+A_OzIi6MG}qEuB3HPo7|y$WQ|xya)KdZ|`2+yLOR^MWTT>gTcdxjJ31c zUYxl*VvUEr1*u2SF@6w$kKhBQt|BWV8P^^uZUU# z)Cl2FKu`F@Q<9OU#kYm<3ncT~RY9zC;Gq7)CybroyJf@T$uO7tlt4>^D$0rq zkcKZslemg>AXL#$$83zT=~xp3n6no81^vV9vNFJb9S!dbb2)UR3EVRT@e%$yf%ph^ z26W@7g*08F1`X@l2f`-mLqdkEB4q6)>xel%zy08r;7Jw$`I(4;a#J zI0m8w!y-?GK*$IP9nzWaph5k+cZV=g>qixSxC7!HG>Q92QC?M9MHwrA7)sZ!T^O=R zh5<1YI$c8IDUy)sxSddkRr2Ke1HkX$SU@DKzMkrbO(%&K^PfRK8B0|L&RD@ZDnTfwS$gM-PpSPZCSrM&~LdnPO##i zrWd%ox#H6_|?^%&;yj}%zy(JUj&#RdQ32z=|i-OK?e z`7#Fx?O!6iAaD;SV_~jU3rpCLgFPf%&6;6;f-jXy=se!+FIFm28pU{01wcIZt?dIKEH?Cj10tr|ZRDx8YAft*r z@jwPv>=J8ho2gBLdRbn+{#kc#T)%!5r)|Np9iKj1<}Z;6JV^-RW$l#mYM;JU` zzufEa?ds~g_aQ+D3$mH`yboDS%25O_MsXl55C?HKY1)Yf2sWGvqyKT zFBKOGhK+O;v{I=|g7hDTtLcO>(P-c|P@BzLiYSL0H!NCmzP#k|uMZ#vi<3vr1~mfv zaOzq@Jb*pItfI}XqsN(SM}xiv;G93tW|KqFxPHOn)5V-$AoZ-Wl$mvsI`NWoCCI#L zNwh)1{iI0*8LRy&RyV+5+d z3G1Y13RJIlbx&vJKl~X^|D4>DO=}^MzZS@pG%Aa2#fmvbqYR>u=C=`^qsQ5t;QkPI9!scMt*jNBlSS{x51u+hl@Du=UDej zXV=47XYW3|Sy@te_SgYx_TmJy#~%z*^Kvh5TYV#4(A@#>b3tm=3V0@mZ~9$2UB_T}6dK$C1eBD{W0$_)1Iqp8IPE!+ ze*4~aGRKZTgH%>pFeGiQU$<)cVmBAK%R0@Qzi{dZJ*_A-qyq>jA-Ryts`;`TwENC- zOinviTX(g*=-lzWaCR0fXEJ?l-AWIa+4gpjR|jWaE1R*ybcaTvp&cPW(MRC6ck5FM zs9$b3C-Lavn|G>83QiwRChr#Eg0tq@)k~f5*_Av8$ei_7}eeIu(gZXx2L-H5+^%-e=KZDGEoyR#K7fFB(i|dx7dRD`0+Y@)q6rG zItGY>gAcJGt#=p8h+Qz>#&+B8q-!@Xu+AOd9m8MfIHzuv=WGE`K_iOeRhX*%5vDdPy!ml9TDPEByW7 z`w9d=4cQ+v@}OxnSgj|v^B0gO!*0eV<2G-PymXb94SzZ$n7j-5`M?=ekPT`+0U!x{ zF2ZL7jc#g6VCXMEQ-nbM=cEm>2z|Ju3;nQZ#I$wBv*OpZaZSjZ+R&t9G8-S0q zy;Zt&l>$3r18&&PaIL8`XPzxMmJqta-Nr&N`}|Z*LGmMY*6*PtD~s)jjm3eTp~Hqw zwy`-|a5Ns!h-uIlK>o&XA#lEv&6iwqtZeBJ`0Nao@1`X^d08IF>jWkUHtcVQ<#v+~m1N#jfYc|SezSG2Jix`q? z$wf>}2~Jc6^oAx84SP*|m{7JixI1+8Xd?rLe*dQYSt|DyaGP!z^}!YZP1*sgQe6Bn z4r%r2*SGgT9mp>xbD0GbL4k2tJ^qFXS!pS{1gu7}QN?&Pq&G$e)dxVzF@`O11-q@H zg5fB+F5tPeq$Euu4wkyOIAa1DgaIxLUa1J-DL}x4IldUZN(8UP#~x|Q-McZm_X5}q z6boQ4oH{IpbAV|QNl0uwG8xgO6;zZMDl~x57_tm$B*g&y48F;u6PWxT0w!0J-^5_i z`ArP}Bfk;a=|<-WbdEsh2y~7>=LmF;K<5Z_jzH%Kv>gHEVa6uymXSJr+e-Yv78$$! zb(1C#U=k{!gY!~&n<2tbL zZF1W$^%7}U(0E_<+v>!ncDW+Z{C)2q$*?=vgp@)g^}Hw8NF3rF_*E;`5mo9jtjV(0q%zGWxm`u~0X{kQMG8+-K? zU3w~ryY?}^*z5nzAAgE2&uTLl_7d9#vF!^NqNs~HA914&Kh{1$q>1@gT7R^CB1ciI zIr!oBi3FNB4*YogjT8U7_K9%No0{ZSz;}f`fmGeb-X> zN7UTg*jEXteE%aff1i#v-vMr3^?iarh%2#G|6Rm6;r;+0$ZxG~@?j$Usp9`=X=%Uv z&Ht!zFxJxk`v0`~mi85I{CDk}fB5g(U;7_7-*Wt8{>%2;ep~pz-JuriztjG|eY{&6 z=p6s14<8ZVmfw1~TGs#E8r1SRC1|U-k4S%O{BH}tS6|Qx$=fYG?6CH??ANt9#wqas zuxN*N1q_|`1ycv_;@C zAD#^S;r8ibY~l}&EzJLr{v*Xx?RK{n;p57d=26=}Le)y5RrR)fD+Od#F8cfMt6%1} zmTvtwp<0=1Q@zqoipBWz9ivE3b_{B59xZEQ;oA*a?ZLMa!xu#)B(+~+gJgW0-Yn! KIRZa^1pXfZSr0V; literal 0 HcmV?d00001 -- 2.45.2