| @@ -31,6 +31,10 @@ | |||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
| #include <Carbon/Carbon.h> | |||
| #include <IOKit/IOKitLib.h> | |||
| #include <IOKit/IOCFPlugIn.h> | |||
| #include <IOKit/hid/IOHIDLib.h> | |||
| #include <IOKit/hid/IOHIDKeys.h> | |||
| #include <fnmatch.h> | |||
| #if JUCE_OPENGL | |||
| @@ -2997,6 +3001,242 @@ const int KeyPress::stopKey = 0x30001; | |||
| const int KeyPress::fastForwardKey = 0x30002; | |||
| const int KeyPress::rewindKey = 0x30003; | |||
| //============================================================================== | |||
| AppleRemoteDevice::AppleRemoteDevice() | |||
| : device (0), | |||
| queue (0), | |||
| remoteId (0) | |||
| { | |||
| } | |||
| AppleRemoteDevice::~AppleRemoteDevice() | |||
| { | |||
| stop(); | |||
| } | |||
| static io_object_t getAppleRemoteDevice() throw() | |||
| { | |||
| CFMutableDictionaryRef dict = IOServiceMatching ("AppleIRController"); | |||
| io_iterator_t iter = 0; | |||
| io_object_t iod = 0; | |||
| if (IOServiceGetMatchingServices (kIOMasterPortDefault, dict, &iter) == kIOReturnSuccess | |||
| && iter != 0) | |||
| { | |||
| iod = IOIteratorNext (iter); | |||
| } | |||
| IOObjectRelease (iter); | |||
| return iod; | |||
| } | |||
| static bool createAppleRemoteInterface (io_object_t iod, void** device) throw() | |||
| { | |||
| jassert (*device == 0); | |||
| io_name_t classname; | |||
| if (IOObjectGetClass (iod, classname) == kIOReturnSuccess) | |||
| { | |||
| IOCFPlugInInterface** cfPlugInInterface = 0; | |||
| SInt32 score = 0; | |||
| if (IOCreatePlugInInterfaceForService (iod, | |||
| kIOHIDDeviceUserClientTypeID, | |||
| kIOCFPlugInInterfaceID, | |||
| &cfPlugInInterface, | |||
| &score) == kIOReturnSuccess) | |||
| { | |||
| HRESULT hr = (*cfPlugInInterface)->QueryInterface (cfPlugInInterface, | |||
| CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), | |||
| device); | |||
| (void) hr; | |||
| (*cfPlugInInterface)->Release (cfPlugInInterface); | |||
| } | |||
| } | |||
| return *device != 0; | |||
| } | |||
| bool AppleRemoteDevice::start (const bool inExclusiveMode) throw() | |||
| { | |||
| if (queue != 0) | |||
| return true; | |||
| stop(); | |||
| bool result = false; | |||
| io_object_t iod = getAppleRemoteDevice(); | |||
| if (iod != 0) | |||
| { | |||
| if (createAppleRemoteInterface (iod, &device) && open (inExclusiveMode)) | |||
| result = true; | |||
| else | |||
| stop(); | |||
| IOObjectRelease (iod); | |||
| } | |||
| return result; | |||
| } | |||
| void AppleRemoteDevice::stop() throw() | |||
| { | |||
| if (queue != 0) | |||
| { | |||
| (*(IOHIDQueueInterface**) queue)->stop ((IOHIDQueueInterface**) queue); | |||
| (*(IOHIDQueueInterface**) queue)->dispose ((IOHIDQueueInterface**) queue); | |||
| (*(IOHIDQueueInterface**) queue)->Release ((IOHIDQueueInterface**) queue); | |||
| queue = 0; | |||
| } | |||
| if (device != 0) | |||
| { | |||
| (*(IOHIDDeviceInterface**) device)->close ((IOHIDDeviceInterface**) device); | |||
| (*(IOHIDDeviceInterface**) device)->Release ((IOHIDDeviceInterface**) device); | |||
| device = 0; | |||
| } | |||
| } | |||
| static void appleRemoteQueueCallback (void* const target, const IOReturn result, void*, void*) | |||
| { | |||
| if (result == kIOReturnSuccess) | |||
| ((AppleRemoteDevice*) target)->handleCallbackInternal(); | |||
| } | |||
| bool AppleRemoteDevice::open (const bool openInExclusiveMode) throw() | |||
| { | |||
| #if ! MACOS_10_2_OR_EARLIER | |||
| Array <int> cookies; | |||
| CFArrayRef elements; | |||
| IOHIDDeviceInterface122** const device122 = (IOHIDDeviceInterface122**) device; | |||
| if ((*device122)->copyMatchingElements (device122, 0, &elements) != kIOReturnSuccess) | |||
| return false; | |||
| for (int i = 0; i < CFArrayGetCount (elements); ++i) | |||
| { | |||
| CFDictionaryRef element = (CFDictionaryRef) CFArrayGetValueAtIndex (elements, i); | |||
| // get the cookie | |||
| CFTypeRef object = CFDictionaryGetValue (element, CFSTR (kIOHIDElementCookieKey)); | |||
| if (object == 0 || CFGetTypeID (object) != CFNumberGetTypeID()) | |||
| continue; | |||
| long number; | |||
| if (! CFNumberGetValue ((CFNumberRef) object, kCFNumberLongType, &number)) | |||
| continue; | |||
| cookies.add ((int) number); | |||
| } | |||
| CFRelease (elements); | |||
| if ((*(IOHIDDeviceInterface**) device) | |||
| ->open ((IOHIDDeviceInterface**) device, | |||
| openInExclusiveMode ? kIOHIDOptionsTypeSeizeDevice | |||
| : kIOHIDOptionsTypeNone) == KERN_SUCCESS) | |||
| { | |||
| queue = (*(IOHIDDeviceInterface**) device)->allocQueue ((IOHIDDeviceInterface**) device); | |||
| if (queue != 0) | |||
| { | |||
| (*(IOHIDQueueInterface**) queue)->create ((IOHIDQueueInterface**) queue, 0, 12); | |||
| for (int i = 0; i < cookies.size(); ++i) | |||
| { | |||
| IOHIDElementCookie cookie = (IOHIDElementCookie) cookies.getUnchecked(i); | |||
| (*(IOHIDQueueInterface**) queue)->addElement ((IOHIDQueueInterface**) queue, cookie, 0); | |||
| } | |||
| CFRunLoopSourceRef eventSource; | |||
| if ((*(IOHIDQueueInterface**) queue) | |||
| ->createAsyncEventSource ((IOHIDQueueInterface**) queue, &eventSource) == KERN_SUCCESS) | |||
| { | |||
| if ((*(IOHIDQueueInterface**) queue)->setEventCallout ((IOHIDQueueInterface**) queue, | |||
| appleRemoteQueueCallback, this, 0) == KERN_SUCCESS) | |||
| { | |||
| CFRunLoopAddSource (CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode); | |||
| (*(IOHIDQueueInterface**) queue)->start ((IOHIDQueueInterface**) queue); | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| #endif | |||
| return false; | |||
| } | |||
| void AppleRemoteDevice::handleCallbackInternal() | |||
| { | |||
| int totalValues = 0; | |||
| AbsoluteTime nullTime = { 0, 0 }; | |||
| char cookies [12]; | |||
| int numCookies = 0; | |||
| while (numCookies < numElementsInArray (cookies)) | |||
| { | |||
| IOHIDEventStruct e; | |||
| if ((*(IOHIDQueueInterface**) queue)->getNextEvent ((IOHIDQueueInterface**) queue, &e, nullTime, 0) != kIOReturnSuccess) | |||
| break; | |||
| if ((int) e.elementCookie == 19) | |||
| { | |||
| remoteId = e.value; | |||
| buttonPressed (switched, false); | |||
| } | |||
| else | |||
| { | |||
| totalValues += e.value; | |||
| cookies [numCookies++] = (char) (pointer_sized_int) e.elementCookie; | |||
| } | |||
| } | |||
| cookies [numCookies++] = 0; | |||
| static const char buttonPatterns[] = | |||
| { | |||
| 14, 7, 6, 5, 14, 7, 6, 5, 0, | |||
| 14, 8, 6, 5, 14, 8, 6, 5, 0, | |||
| 14, 12, 11, 6, 5, 0, | |||
| 14, 13, 11, 6, 5, 0, | |||
| 14, 9, 6, 5, 14, 9, 6, 5, 0, | |||
| 14, 10, 6, 5, 14, 10, 6, 5, 0, | |||
| 14, 6, 5, 4, 2, 0, | |||
| 14, 6, 5, 3, 2, 0, | |||
| 14, 6, 5, 14, 6, 5, 0, | |||
| 18, 14, 6, 5, 18, 14, 6, 5, 0, | |||
| 19, 0 | |||
| }; | |||
| int buttonNum = (int) menuButton; | |||
| int i = 0; | |||
| while (i < numElementsInArray (buttonPatterns)) | |||
| { | |||
| if (strcmp (cookies, buttonPatterns + i) == 0) | |||
| { | |||
| buttonPressed ((ButtonType) buttonNum, totalValues > 0); | |||
| break; | |||
| } | |||
| i += strlen (buttonPatterns + i) + 1; | |||
| ++buttonNum; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_OPENGL | |||
| @@ -6,6 +6,7 @@ | |||
| ============================================================================== | |||
| Changelist for version 1.44 | |||
| - new Mac-only class: AppleRemoteDevice, which lets you grab and listen for events from your Apple remote control. | |||
| - change to the keyPressed() and keyStateChanged() callbacks in Component and KeyListener. These used to be void, but they now return a bool to indicate whether the key event was needed or not. Any existing code you've got will break in the compiler, so just change it to return true if the key was used, or false to allow the event to be passed up to the next component in the chain. (This change is a better architecture than before, and was also needed so that plugins can allow unused key events to be passed on to the host application) | |||
| - swapped the look and feel classes around, so that the basic LookAndFeel class is now what used to be the "shiny" one. The ShinyLookAndFeel class has been removed, and for that old fashioned look, I've added an OldSchoolLookAndFeel that you can use if you need the original L+F. This means that any custom looks that you were using may need to change their base class. | |||
| - changed the MouseEvent structure so that it now contains a pointer to the event component and also the original component. | |||
| @@ -1,178 +1,178 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "jucedemo_headers.h" | |||
| #include "MainDemoWindow.h" | |||
| //============================================================================== | |||
| class JUCEDemoApplication : public JUCEApplication | |||
| { | |||
| /* Important! NEVER embed objects directly inside your JUCEApplication class! Use | |||
| ONLY pointers to objects, which you should create during the initialise() method | |||
| (NOT in the constructor!) and delete in the shutdown() method (NOT in the | |||
| destructor!) | |||
| This is because the application object gets created before Juce has been properly | |||
| initialised, so any embedded objects would also get constructed too soon. | |||
| */ | |||
| MainDemoWindow* theMainWindow; | |||
| public: | |||
| //============================================================================== | |||
| JUCEDemoApplication() | |||
| : theMainWindow (0) | |||
| { | |||
| // NEVER do anything in here that could involve any Juce function being called | |||
| // - leave all your startup tasks until the initialise() method. | |||
| } | |||
| ~JUCEDemoApplication() | |||
| { | |||
| // Your shutdown() method should already have done all the things necessary to | |||
| // clean up this app object, so you should never need to put anything in | |||
| // the destructor. | |||
| // Making any Juce calls in here could be very dangerous... | |||
| } | |||
| //============================================================================== | |||
| void initialise (const String& commandLine) | |||
| { | |||
| // just create the main window... | |||
| theMainWindow = new MainDemoWindow(); | |||
| theMainWindow->centreWithSize (700, 600); | |||
| theMainWindow->setVisible (true); | |||
| // this little function just demonstrates a few system info calls | |||
| Logger::outputDebugString (collectSomeSystemInfo()); | |||
| /* on return from this method, the app will go into its the main event | |||
| dispatch loop, and this will run until something calls | |||
| JUCEAppliction::quit(). | |||
| In this case, JUCEAppliction::quit() will be called by the | |||
| demo window when the user clicks on its close button. | |||
| */ | |||
| } | |||
| void shutdown() | |||
| { | |||
| delete theMainWindow; | |||
| theMainWindow = 0; | |||
| } | |||
| //============================================================================== | |||
| const String getApplicationName() | |||
| { | |||
| return T("JUCE Demo"); | |||
| } | |||
| const String getApplicationVersion() | |||
| { | |||
| return T("1.0"); | |||
| } | |||
| bool moreThanOneInstanceAllowed() | |||
| { | |||
| return true; | |||
| } | |||
| void anotherInstanceStarted (const String& commandLine) | |||
| { | |||
| // This will get called if the user launches another copy of the app, but | |||
| // there's nothing that the demo app needs to do here. | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| // this little function just demonstrates a few system info calls | |||
| static const String collectSomeSystemInfo() | |||
| { | |||
| String systemInfo; | |||
| systemInfo | |||
| << T("Time and date: ") << Time::getCurrentTime().toString (true, true) | |||
| << T("\nOperating system: ") << SystemStats::getOperatingSystemName() | |||
| << T("\nCPU vendor: ") << SystemStats::getCpuVendor() | |||
| << T("\nCPU speed: ") << SystemStats::getCpuSpeedInMegaherz() << T("MHz\n") | |||
| << T("\nNumber of physical CPUs: ") << SystemStats::getNumPhysicalCpus() | |||
| << T("\nNumber of logical CPUs: ") << SystemStats::getNumLogicalCpus() | |||
| << T("\nCPU has MMX: ") << (SystemStats::hasMMX() ? T("yes") : T("no")) | |||
| << T("\nCPU has SSE: ") << (SystemStats::hasSSE() ? T("yes") : T("no")) | |||
| << T("\nCPU has SSE2: ") << (SystemStats::hasSSE2() ? T("yes") : T("no")) | |||
| << T("\nCPU has 3DNOW: ") << (SystemStats::has3DNow() ? T("yes") : T("no")) | |||
| << T("\nCPU has hyperthreading: ") << (SystemStats::hasHyperThreading() ? T("yes") : T("no")) | |||
| << T("\nMemory size: ") << SystemStats::getMemorySizeInMegabytes() << T("MB\n"); | |||
| int64 macAddresses[8]; | |||
| const int numAddresses = SystemStats::getMACAddresses (macAddresses, 8); | |||
| for (int i = 0; i < numAddresses; ++i) | |||
| { | |||
| systemInfo | |||
| << T("Found network card MAC address: ") | |||
| << String::formatted (T("%02x-%02x-%02x-%02x-%02x-%02x\n"), | |||
| 0xff & (int) (macAddresses [i] >> 40), | |||
| 0xff & (int) (macAddresses [i] >> 32), | |||
| 0xff & (int) (macAddresses [i] >> 24), | |||
| 0xff & (int) (macAddresses [i] >> 16), | |||
| 0xff & (int) (macAddresses [i] >> 8), | |||
| 0xff & (int) macAddresses [i]); | |||
| } | |||
| systemInfo | |||
| << T("Current executable file: ") | |||
| << File::getSpecialLocation (File::currentExecutableFile).getFullPathName() | |||
| << T("\nCurrent application file: ") | |||
| << File::getSpecialLocation (File::currentApplicationFile).getFullPathName() | |||
| << T("\nUser home directory: ") | |||
| << File::getSpecialLocation (File::userHomeDirectory).getFullPathName() | |||
| << T("\nUser documents directory: ") | |||
| << File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName() | |||
| << T("\nUser application data directory: ") | |||
| << File::getSpecialLocation (File::userApplicationDataDirectory).getFullPathName() | |||
| << T("\nCommon application data directory: ") | |||
| << File::getSpecialLocation (File::commonApplicationDataDirectory).getFullPathName() | |||
| << T("\nTemp directory: ") | |||
| << File::getSpecialLocation (File::tempDirectory).getFullPathName() | |||
| << T("\n\n"); | |||
| return systemInfo; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| /* | |||
| This macro creates the application's main() function.. | |||
| */ | |||
| START_JUCE_APPLICATION (JUCEDemoApplication) | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "jucedemo_headers.h" | |||
| #include "MainDemoWindow.h" | |||
| //============================================================================== | |||
| class JUCEDemoApplication : public JUCEApplication | |||
| { | |||
| /* Important! NEVER embed objects directly inside your JUCEApplication class! Use | |||
| ONLY pointers to objects, which you should create during the initialise() method | |||
| (NOT in the constructor!) and delete in the shutdown() method (NOT in the | |||
| destructor!) | |||
| This is because the application object gets created before Juce has been properly | |||
| initialised, so any embedded objects would also get constructed too soon. | |||
| */ | |||
| MainDemoWindow* theMainWindow; | |||
| public: | |||
| //============================================================================== | |||
| JUCEDemoApplication() | |||
| : theMainWindow (0) | |||
| { | |||
| // NEVER do anything in here that could involve any Juce function being called | |||
| // - leave all your startup tasks until the initialise() method. | |||
| } | |||
| ~JUCEDemoApplication() | |||
| { | |||
| // Your shutdown() method should already have done all the things necessary to | |||
| // clean up this app object, so you should never need to put anything in | |||
| // the destructor. | |||
| // Making any Juce calls in here could be very dangerous... | |||
| } | |||
| //============================================================================== | |||
| void initialise (const String& commandLine) | |||
| { | |||
| // just create the main window... | |||
| theMainWindow = new MainDemoWindow(); | |||
| theMainWindow->centreWithSize (700, 600); | |||
| theMainWindow->setVisible (true); | |||
| // this little function just demonstrates a few system info calls | |||
| Logger::outputDebugString (collectSomeSystemInfo()); | |||
| /* on return from this method, the app will go into its the main event | |||
| dispatch loop, and this will run until something calls | |||
| JUCEAppliction::quit(). | |||
| In this case, JUCEAppliction::quit() will be called by the | |||
| demo window when the user clicks on its close button. | |||
| */ | |||
| } | |||
| void shutdown() | |||
| { | |||
| delete theMainWindow; | |||
| theMainWindow = 0; | |||
| } | |||
| //============================================================================== | |||
| const String getApplicationName() | |||
| { | |||
| return T("JUCE Demo"); | |||
| } | |||
| const String getApplicationVersion() | |||
| { | |||
| return T("1.0"); | |||
| } | |||
| bool moreThanOneInstanceAllowed() | |||
| { | |||
| return true; | |||
| } | |||
| void anotherInstanceStarted (const String& commandLine) | |||
| { | |||
| // This will get called if the user launches another copy of the app, but | |||
| // there's nothing that the demo app needs to do here. | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| // this little function just demonstrates a few system info calls | |||
| static const String collectSomeSystemInfo() | |||
| { | |||
| String systemInfo; | |||
| systemInfo | |||
| << T("Time and date: ") << Time::getCurrentTime().toString (true, true) | |||
| << T("\nOperating system: ") << SystemStats::getOperatingSystemName() | |||
| << T("\nCPU vendor: ") << SystemStats::getCpuVendor() | |||
| << T("\nCPU speed: ") << SystemStats::getCpuSpeedInMegaherz() << T("MHz\n") | |||
| << T("\nNumber of physical CPUs: ") << SystemStats::getNumPhysicalCpus() | |||
| << T("\nNumber of logical CPUs: ") << SystemStats::getNumLogicalCpus() | |||
| << T("\nCPU has MMX: ") << (SystemStats::hasMMX() ? T("yes") : T("no")) | |||
| << T("\nCPU has SSE: ") << (SystemStats::hasSSE() ? T("yes") : T("no")) | |||
| << T("\nCPU has SSE2: ") << (SystemStats::hasSSE2() ? T("yes") : T("no")) | |||
| << T("\nCPU has 3DNOW: ") << (SystemStats::has3DNow() ? T("yes") : T("no")) | |||
| << T("\nCPU has hyperthreading: ") << (SystemStats::hasHyperThreading() ? T("yes") : T("no")) | |||
| << T("\nMemory size: ") << SystemStats::getMemorySizeInMegabytes() << T("MB\n"); | |||
| int64 macAddresses[8]; | |||
| const int numAddresses = SystemStats::getMACAddresses (macAddresses, 8); | |||
| for (int i = 0; i < numAddresses; ++i) | |||
| { | |||
| systemInfo | |||
| << T("Found network card MAC address: ") | |||
| << String::formatted (T("%02x-%02x-%02x-%02x-%02x-%02x\n"), | |||
| 0xff & (int) (macAddresses [i] >> 40), | |||
| 0xff & (int) (macAddresses [i] >> 32), | |||
| 0xff & (int) (macAddresses [i] >> 24), | |||
| 0xff & (int) (macAddresses [i] >> 16), | |||
| 0xff & (int) (macAddresses [i] >> 8), | |||
| 0xff & (int) macAddresses [i]); | |||
| } | |||
| systemInfo | |||
| << T("Current executable file: ") | |||
| << File::getSpecialLocation (File::currentExecutableFile).getFullPathName() | |||
| << T("\nCurrent application file: ") | |||
| << File::getSpecialLocation (File::currentApplicationFile).getFullPathName() | |||
| << T("\nUser home directory: ") | |||
| << File::getSpecialLocation (File::userHomeDirectory).getFullPathName() | |||
| << T("\nUser documents directory: ") | |||
| << File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName() | |||
| << T("\nUser application data directory: ") | |||
| << File::getSpecialLocation (File::userApplicationDataDirectory).getFullPathName() | |||
| << T("\nCommon application data directory: ") | |||
| << File::getSpecialLocation (File::commonApplicationDataDirectory).getFullPathName() | |||
| << T("\nTemp directory: ") | |||
| << File::getSpecialLocation (File::tempDirectory).getFullPathName() | |||
| << T("\n\n"); | |||
| return systemInfo; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| /* | |||
| This macro creates the application's main() function.. | |||
| */ | |||
| START_JUCE_APPLICATION (JUCEDemoApplication) | |||
| @@ -1024,6 +1024,85 @@ public: | |||
| } | |||
| }; | |||
| #if JUCE_MAC | |||
| //============================================================================== | |||
| /** This pops open a dialog box and waits for you to press keys on your Apple Remote, | |||
| which it describes in the box. | |||
| */ | |||
| class AppleRemoteTestWindow : public AlertWindow, | |||
| public AppleRemoteDevice | |||
| { | |||
| public: | |||
| AppleRemoteTestWindow() | |||
| : AlertWindow ("Apple Remote Control Test!", | |||
| "If you've got an Apple Remote, press some buttons now...", | |||
| AlertWindow::NoIcon) | |||
| { | |||
| addButton (T("done"), 0); | |||
| // (To open the device in non-exclusive mode, pass 'false' in here).. | |||
| if (! start (true)) | |||
| setMessage ("Couldn't open the remote control device!"); | |||
| } | |||
| ~AppleRemoteTestWindow() | |||
| { | |||
| stop(); | |||
| } | |||
| void buttonPressed (const ButtonType buttonId, const bool isDown) | |||
| { | |||
| String desc; | |||
| switch (buttonId) | |||
| { | |||
| case menuButton: | |||
| desc = "menu button (short)"; | |||
| break; | |||
| case playButton: | |||
| desc = "play button"; | |||
| break; | |||
| case plusButton: | |||
| desc = "plus button"; | |||
| break; | |||
| case minusButton: | |||
| desc = "minus button"; | |||
| break; | |||
| case rightButton: | |||
| desc = "right button (short)"; | |||
| break; | |||
| case leftButton: | |||
| desc = "left button (short)"; | |||
| break; | |||
| case rightButton_Long: | |||
| desc = "right button (long)"; | |||
| break; | |||
| case leftButton_Long: | |||
| desc = "left button (long)"; | |||
| break; | |||
| case menuButton_Long: | |||
| desc = "menu button (long)"; | |||
| break; | |||
| case playButtonSleepMode: | |||
| desc = "play (sleep mode)"; | |||
| break; | |||
| case switched: | |||
| desc = "remote switched"; | |||
| break; | |||
| } | |||
| if (isDown) | |||
| desc << " -- [down]"; | |||
| else | |||
| desc << " -- [up]"; | |||
| setMessage (desc); | |||
| } | |||
| }; | |||
| #endif | |||
| //============================================================================== | |||
| const int numGroups = 4; | |||
| @@ -1085,67 +1164,71 @@ public: | |||
| else if (button == menuButton) | |||
| { | |||
| PopupMenu m; | |||
| m.addItem (1, T("normal item")); | |||
| m.addItem (2, T("disabled item"), false); | |||
| m.addItem (3, T("ticked item"), true, true); | |||
| m.addColouredItem (4, T("coloured item"), Colours::green); | |||
| m.addItem (1, T("Normal item")); | |||
| m.addItem (2, T("Disabled item"), false); | |||
| m.addItem (3, T("Ticked item"), true, true); | |||
| m.addColouredItem (4, T("Coloured item"), Colours::green); | |||
| m.addSeparator(); | |||
| m.addCustomItem (5, new CustomMenuComponent()); | |||
| m.addSeparator(); | |||
| PopupMenu tabsMenu; | |||
| tabsMenu.addItem (1001, T("show tabs at the top"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtTop); | |||
| tabsMenu.addItem (1002, T("show tabs at the bottom"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtBottom); | |||
| tabsMenu.addItem (1003, T("show tabs at the left"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtLeft); | |||
| tabsMenu.addItem (1004, T("show tabs at the right"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtRight); | |||
| m.addSubMenu (T("tab position"), tabsMenu); | |||
| tabsMenu.addItem (1001, T("Show tabs at the top"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtTop); | |||
| tabsMenu.addItem (1002, T("Show tabs at the bottom"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtBottom); | |||
| tabsMenu.addItem (1003, T("Show tabs at the left"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtLeft); | |||
| tabsMenu.addItem (1004, T("Show tabs at the right"), true, tabs->getOrientation() == TabbedButtonBar::TabsAtRight); | |||
| m.addSubMenu (T("Tab position"), tabsMenu); | |||
| m.addSeparator(); | |||
| PopupMenu dialogMenu; | |||
| dialogMenu.addItem (100, T("show a plain alert-window...")); | |||
| dialogMenu.addItem (101, T("show an alert-window with a 'warning' icon...")); | |||
| dialogMenu.addItem (102, T("show an alert-window with an 'info' icon...")); | |||
| dialogMenu.addItem (103, T("show an alert-window with a 'question' icon...")); | |||
| dialogMenu.addItem (100, T("Show a plain alert-window...")); | |||
| dialogMenu.addItem (101, T("Show an alert-window with a 'warning' icon...")); | |||
| dialogMenu.addItem (102, T("Show an alert-window with an 'info' icon...")); | |||
| dialogMenu.addItem (103, T("Show an alert-window with a 'question' icon...")); | |||
| dialogMenu.addSeparator(); | |||
| dialogMenu.addItem (110, T("show an ok/cancel alert-window...")); | |||
| dialogMenu.addItem (110, T("Show an ok/cancel alert-window...")); | |||
| dialogMenu.addSeparator(); | |||
| dialogMenu.addItem (111, T("show an alert-window with some extra components...")); | |||
| dialogMenu.addItem (111, T("Show an alert-window with some extra components...")); | |||
| dialogMenu.addSeparator(); | |||
| dialogMenu.addItem (112, T("show a ThreadWithProgressWindow demo...")); | |||
| dialogMenu.addItem (112, T("Show a ThreadWithProgressWindow demo...")); | |||
| m.addSubMenu (T("AlertWindow demonstrations"), dialogMenu); | |||
| dialogMenu.addSeparator(); | |||
| m.addSeparator(); | |||
| m.addItem (120, T("show a colour selector demo...")); | |||
| m.addItem (120, T("Show a colour selector demo...")); | |||
| m.addSeparator(); | |||
| dialogMenu.addSeparator(); | |||
| #if JUCE_MAC | |||
| m.addItem (140, T("Run the Apple Remote Control test...")); | |||
| m.addSeparator(); | |||
| #endif | |||
| PopupMenu nativeFileChoosers; | |||
| nativeFileChoosers.addItem (121, T("'load' file browser...")); | |||
| nativeFileChoosers.addItem (124, T("'load' file browser with an image file preview...")); | |||
| nativeFileChoosers.addItem (122, T("'save' file browser...")); | |||
| nativeFileChoosers.addItem (123, T("choose directory file browser...")); | |||
| nativeFileChoosers.addItem (121, T("'Load' file browser...")); | |||
| nativeFileChoosers.addItem (124, T("'Load' file browser with an image file preview...")); | |||
| nativeFileChoosers.addItem (122, T("'Save' file browser...")); | |||
| nativeFileChoosers.addItem (123, T("'Choose directory' file browser...")); | |||
| PopupMenu juceFileChoosers; | |||
| juceFileChoosers.addItem (131, T("'load' file browser...")); | |||
| juceFileChoosers.addItem (134, T("'load' file browser with an image file preview...")); | |||
| juceFileChoosers.addItem (132, T("'save' file browser...")); | |||
| juceFileChoosers.addItem (133, T("choose directory file browser...")); | |||
| juceFileChoosers.addItem (131, T("'Load' file browser...")); | |||
| juceFileChoosers.addItem (134, T("'Load' file browser with an image file preview...")); | |||
| juceFileChoosers.addItem (132, T("'Save' file browser...")); | |||
| juceFileChoosers.addItem (133, T("'Choose directory' file browser...")); | |||
| PopupMenu fileChoosers; | |||
| fileChoosers.addSubMenu (T("Operating system dialogs"), nativeFileChoosers); | |||
| fileChoosers.addSubMenu (T("Juce dialogs"), juceFileChoosers); | |||
| m.addSubMenu (T("file chooser dialogs"), fileChoosers); | |||
| m.addSubMenu (T("File chooser dialogs"), fileChoosers); | |||
| int result = m.showAt (menuButton); | |||
| @@ -1232,6 +1315,13 @@ public: | |||
| // method causes the loop to exit. | |||
| colourDialog.runModalLoop(); | |||
| } | |||
| else if (result == 140) | |||
| { | |||
| #if JUCE_MAC | |||
| AppleRemoteTestWindow test; | |||
| test.runModalLoop(); | |||
| #endif | |||
| } | |||
| else if (result >= 121 && result < 139) | |||
| { | |||
| const bool useNativeVersion = result < 130; | |||
| @@ -150,4 +150,88 @@ public: | |||
| }; | |||
| #if JUCE_MAC | |||
| //============================================================================== | |||
| /** | |||
| A wrapper class for picking up events from an Apple IR remote control device. | |||
| To use it, just create a subclass of this class, implementing the buttonPressed() | |||
| callback, then call start() and stop() to start or stop receiving events. | |||
| */ | |||
| class JUCE_API AppleRemoteDevice | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| AppleRemoteDevice(); | |||
| ~AppleRemoteDevice(); | |||
| //============================================================================== | |||
| /** The set of buttons that may be pressed. | |||
| @see buttonPressed | |||
| */ | |||
| enum ButtonType | |||
| { | |||
| menuButton = 0, /**< The menu button (if it's held for a short time). */ | |||
| playButton, /**< The play button. */ | |||
| plusButton, /**< The plus or volume-up button. */ | |||
| minusButton, /**< The minus or volume-down button. */ | |||
| rightButton, /**< The right button (if it's held for a short time). */ | |||
| leftButton, /**< The left button (if it's held for a short time). */ | |||
| rightButton_Long, /**< The right button (if it's held for a long time). */ | |||
| leftButton_Long, /**< The menu button (if it's held for a long time). */ | |||
| menuButton_Long, /**< The menu button (if it's held for a long time). */ | |||
| playButtonSleepMode, | |||
| switched | |||
| }; | |||
| //============================================================================== | |||
| /** Override this method to receive the callback about a button press. | |||
| The callback will happen on the application's message thread. | |||
| Some buttons trigger matching up and down events, in which the isDown parameter | |||
| will be true and then false. Others only send a single event when the | |||
| button is pressed. | |||
| */ | |||
| virtual void buttonPressed (const ButtonType buttonId, const bool isDown) = 0; | |||
| //============================================================================== | |||
| /** Starts the device running and responding to events. | |||
| Returns true if it managed to open the device. | |||
| @param inExclusiveMode if true, the remote will be grabbed exclusively for this app, | |||
| and will not be available to any other part of the system. If | |||
| false, it will be shared with other apps. | |||
| @see stop | |||
| */ | |||
| bool start (const bool inExclusiveMode) throw(); | |||
| /** Stops the device running. | |||
| @see start | |||
| */ | |||
| void stop() throw(); | |||
| /** Returns the ID number of the remote, if it has sent one. | |||
| */ | |||
| int getRemoteId() const throw() { return remoteId; } | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| /** @internal */ | |||
| void handleCallbackInternal(); | |||
| private: | |||
| void* device; | |||
| void* queue; | |||
| int remoteId; | |||
| bool open (const bool openInExclusiveMode) throw(); | |||
| }; | |||
| #endif | |||
| #endif // __JUCE_PLATFORMUTILITIES_JUCEHEADER__ | |||