diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
index 8e7f31d6ee..6454152384 100644
--- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
+++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
@@ -798,6 +798,7 @@
C286C779DD52C29F86E3DBE9 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DialogWindow.h; path = ../../src/gui/components/windows/juce_DialogWindow.h; sourceTree = SOURCE_ROOT; };
090907E4FE95EE2B11C1A0E1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DocumentWindow.cpp; path = ../../src/gui/components/windows/juce_DocumentWindow.cpp; sourceTree = SOURCE_ROOT; };
6E522DF13EC47755234A5D57 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DocumentWindow.h; path = ../../src/gui/components/windows/juce_DocumentWindow.h; sourceTree = SOURCE_ROOT; };
+ 3FC6FC331B4E104D1DC223D6 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_NativeMessageBox.h; path = ../../src/gui/components/windows/juce_NativeMessageBox.h; sourceTree = SOURCE_ROOT; };
2E4A5F7128313C23AD0356F7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ResizableWindow.cpp; path = ../../src/gui/components/windows/juce_ResizableWindow.cpp; sourceTree = SOURCE_ROOT; };
207CDD87107EAC8ED17DD601 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ResizableWindow.h; path = ../../src/gui/components/windows/juce_ResizableWindow.h; sourceTree = SOURCE_ROOT; };
87E57C8F3448D615271CD9F6 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_SplashScreen.cpp; path = ../../src/gui/components/windows/juce_SplashScreen.cpp; sourceTree = SOURCE_ROOT; };
@@ -1568,6 +1569,7 @@
C286C779DD52C29F86E3DBE9,
090907E4FE95EE2B11C1A0E1,
6E522DF13EC47755234A5D57,
+ 3FC6FC331B4E104D1DC223D6,
2E4A5F7128313C23AD0356F7,
207CDD87107EAC8ED17DD601,
87E57C8F3448D615271CD9F6,
diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj
index 60d72cbba9..a700feef71 100644
--- a/Builds/VisualStudio2005/Juce.vcproj
+++ b/Builds/VisualStudio2005/Juce.vcproj
@@ -674,6 +674,7 @@
+
diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj
index 3d5b0f1950..5b9ecf6a92 100644
--- a/Builds/VisualStudio2008/Juce.vcproj
+++ b/Builds/VisualStudio2008/Juce.vcproj
@@ -674,6 +674,7 @@
+
diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj
index 1d73cd4d26..0767d2efa9 100644
--- a/Builds/VisualStudio2008_DLL/Juce.vcproj
+++ b/Builds/VisualStudio2008_DLL/Juce.vcproj
@@ -676,6 +676,7 @@
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj
index d6f2eac146..da52c95bae 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj
+++ b/Builds/VisualStudio2010/Juce.vcxproj
@@ -688,6 +688,7 @@
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters
index edb03db9e5..51f271e6fe 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj.filters
+++ b/Builds/VisualStudio2010/Juce.vcxproj.filters
@@ -1998,6 +1998,9 @@
Juce\Source\gui\components\windows
+
+ Juce\Source\gui\components\windows
+
Juce\Source\gui\components\windows
diff --git a/Builds/iOS/Juce.xcodeproj/project.pbxproj b/Builds/iOS/Juce.xcodeproj/project.pbxproj
index b1c910ebc2..424c0b75d0 100644
--- a/Builds/iOS/Juce.xcodeproj/project.pbxproj
+++ b/Builds/iOS/Juce.xcodeproj/project.pbxproj
@@ -798,6 +798,7 @@
C286C779DD52C29F86E3DBE9 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DialogWindow.h; path = ../../src/gui/components/windows/juce_DialogWindow.h; sourceTree = SOURCE_ROOT; };
090907E4FE95EE2B11C1A0E1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DocumentWindow.cpp; path = ../../src/gui/components/windows/juce_DocumentWindow.cpp; sourceTree = SOURCE_ROOT; };
6E522DF13EC47755234A5D57 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DocumentWindow.h; path = ../../src/gui/components/windows/juce_DocumentWindow.h; sourceTree = SOURCE_ROOT; };
+ 3FC6FC331B4E104D1DC223D6 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_NativeMessageBox.h; path = ../../src/gui/components/windows/juce_NativeMessageBox.h; sourceTree = SOURCE_ROOT; };
2E4A5F7128313C23AD0356F7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_ResizableWindow.cpp; path = ../../src/gui/components/windows/juce_ResizableWindow.cpp; sourceTree = SOURCE_ROOT; };
207CDD87107EAC8ED17DD601 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ResizableWindow.h; path = ../../src/gui/components/windows/juce_ResizableWindow.h; sourceTree = SOURCE_ROOT; };
87E57C8F3448D615271CD9F6 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_SplashScreen.cpp; path = ../../src/gui/components/windows/juce_SplashScreen.cpp; sourceTree = SOURCE_ROOT; };
@@ -1568,6 +1569,7 @@
C286C779DD52C29F86E3DBE9,
090907E4FE95EE2B11C1A0E1,
6E522DF13EC47755234A5D57,
+ 3FC6FC331B4E104D1DC223D6,
2E4A5F7128313C23AD0356F7,
207CDD87107EAC8ED17DD601,
87E57C8F3448D615271CD9F6,
diff --git a/Juce.jucer b/Juce.jucer
index ba30de1431..849df70a53 100644
--- a/Juce.jucer
+++ b/Juce.jucer
@@ -965,6 +965,8 @@
file="src/gui/components/windows/juce_DocumentWindow.cpp"/>
+
& selectedNodes)
+void ProjectTreeViewBase::getAllSelectedNodesInTree (Component* componentInTree, OwnedArray & selectedNodes)
{
TreeView* tree = dynamic_cast (componentInTree);
@@ -450,13 +450,41 @@ void ProjectTreeViewBase::showMultiSelectionPopupMenu()
void ProjectTreeViewBase::itemDoubleClicked (const MouseEvent& e)
{
- showDocument();
+ invokeShowDocument();
}
+class TreeviewItemSelectionTimer : public Timer
+{
+public:
+ TreeviewItemSelectionTimer (ProjectTreeViewBase& owner_)
+ : owner (owner_)
+ {}
+
+ void timerCallback()
+ {
+ owner.invokeShowDocument();
+ }
+
+private:
+ ProjectTreeViewBase& owner;
+
+ JUCE_DECLARE_NON_COPYABLE (TreeviewItemSelectionTimer);
+};
+
void ProjectTreeViewBase::itemSelectionChanged (bool isNowSelected)
{
if (isNowSelected)
- showDocument();
+ {
+ delayedSelectionTimer = new TreeviewItemSelectionTimer (*this);
+
+ // for images, give the user longer to start dragging before assuming they're
+ // clicking to select it for previewing..
+ delayedSelectionTimer->startTimer (item.isImageFile() ? 250 : 120);
+ }
+ else
+ {
+ delayedSelectionTimer = 0;
+ }
}
const String ProjectTreeViewBase::getTooltip()
@@ -466,9 +494,16 @@ const String ProjectTreeViewBase::getTooltip()
const String ProjectTreeViewBase::getDragSourceDescription()
{
+ delayedSelectionTimer = 0;
return projectItemDragType;
}
+void ProjectTreeViewBase::invokeShowDocument()
+{
+ delayedSelectionTimer = 0;
+ showDocument();
+}
+
//==============================================================================
ProjectTreeViewBase* ProjectTreeViewBase::getParentProjectItem() const
{
diff --git a/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h b/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h
index a8fad6f5bb..89b2171e73 100644
--- a/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h
+++ b/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.h
@@ -65,6 +65,7 @@ public:
virtual void addFiles (const StringArray& files, int insertIndex);
virtual void moveSelectedItemsTo (OwnedArray & selectedNodes, int insertIndex);
virtual void showMultiSelectionPopupMenu();
+ void invokeShowDocument();
virtual ProjectTreeViewBase* findTreeViewItem (const Project::Item& itemToFind);
@@ -94,11 +95,14 @@ public:
bool isInterestedInDragSource (const String& sourceDescription, Component* sourceComponent);
void itemDropped (const String& sourceDescription, Component* sourceComponent, int insertIndex);
+ static void getAllSelectedNodesInTree (Component* componentInTree, OwnedArray & selectedNodes);
+
//==============================================================================
Project::Item item;
protected:
bool isFileMissing;
+ ScopedPointer delayedSelectionTimer;
//==============================================================================
void treeChildrenChanged (const ValueTree& parentTree);
diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp
index 8d04f230ad..cd7a667525 100644
--- a/juce_amalgamated.cpp
+++ b/juce_amalgamated.cpp
@@ -185,7 +185,7 @@
#endif
#endif
- #if ! JUCE_VC7_OR_EARLIER && ! defined (__INTEL_COMPILER)
+ #if ! JUCE_VC7_OR_EARLIER
#define JUCE_USE_INTRINSICS 1
#endif
#else
@@ -40418,6 +40418,10 @@ void Component::setBounds (const int x, const int y, int w, int h)
else if (! flags.hasHeavyweightPeerFlag)
repaintParent();
}
+ else
+ {
+ bufferedImage = Image::null;
+ }
if (flags.hasHeavyweightPeerFlag)
{
@@ -41040,10 +41044,8 @@ bool Component::isCurrentlyBlockedByAnotherModalComponent() const
{
Component* const mc = getCurrentlyModalComponent();
- return mc != 0
- && mc != this
- && (! mc->isParentOf (this))
- && ! mc->canModalEventBeSentToComponent (this);
+ return ! (mc == 0 || mc == this || mc->isParentOf (this)
+ || mc->canModalEventBeSentToComponent (this));
}
int JUCE_CALLTYPE Component::getNumCurrentlyModalComponents() throw()
@@ -42007,7 +42009,9 @@ void Component::internalBroughtToFront()
Component* const cm = getCurrentlyModalComponent();
if (cm != 0 && cm->getTopLevelComponent() != getTopLevelComponent())
- ModalComponentManager::getInstance()->bringModalComponentsToFront();
+ ModalComponentManager::getInstance()->bringModalComponentsToFront (false); // very important that this is false, otherwise in win32,
+ // non-front components can't get focus when another modal comp is
+ // active, and therefore can't receive mouse-clicks
}
void Component::focusGained (FocusChangeType)
@@ -42981,7 +42985,7 @@ void ModalComponentManager::handleAsyncUpdate()
}
}
-void ModalComponentManager::bringModalComponentsToFront()
+void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
{
ComponentPeer* lastOne = 0;
@@ -42998,8 +43002,10 @@ void ModalComponentManager::bringModalComponentsToFront()
{
if (lastOne == 0)
{
- peer->toFront (true);
- peer->grabFocus();
+ peer->toFront (topOneShouldGrabFocus);
+
+ if (topOneShouldGrabFocus)
+ peer->grabFocus();
}
else
peer->toBehind (lastOne);
@@ -65205,6 +65211,7 @@ namespace LookAndFeelHelpers
}
LookAndFeel::LookAndFeel()
+ : useNativeAlertWindows (false)
{
/* if this fails it means you're trying to create a LookAndFeel object before
the static Colours have been initialised. That ain't gonna work. It probably
@@ -65731,6 +65738,20 @@ const Font LookAndFeel::getAlertWindowFont()
return Font (12.0f);
}
+void LookAndFeel::setUsingNativeAlertWindows (bool shouldUseNativeAlerts)
+{
+ useNativeAlertWindows = shouldUseNativeAlerts;
+}
+
+bool LookAndFeel::isUsingNativeAlertWindows()
+{
+ #if JUCE_LINUX
+ return false; // not available currently..
+ #else
+ return useNativeAlertWindows;
+ #endif
+}
+
void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar,
int width, int height,
double progress, const String& textToShow)
@@ -66712,7 +66733,6 @@ class SliderLabelComp : public Label
{
public:
SliderLabelComp() : Label (String::empty, String::empty) {}
- ~SliderLabelComp() {}
void mouseWheelMove (const MouseEvent&, float, float) {}
};
@@ -76947,10 +76967,17 @@ void AlertWindow::showMessageBox (AlertIconType iconType,
const String& buttonText,
Component* associatedComponent)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, true);
- info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ NativeMessageBox::showMessageBox (iconType, title, message, associatedComponent);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, true);
+ info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
- info.invoke();
+ info.invoke();
+ }
}
#endif
@@ -76960,10 +76987,17 @@ void AlertWindow::showMessageBoxAsync (AlertIconType iconType,
const String& buttonText,
Component* associatedComponent)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, false);
- info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ return NativeMessageBox::showMessageBoxAsync (iconType, title, message, associatedComponent);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, false);
+ info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
- info.invoke();
+ info.invoke();
+ }
}
bool AlertWindow::showOkCancelBox (AlertIconType iconType,
@@ -76974,11 +77008,18 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType,
Component* associatedComponent,
ModalComponentManager::Callback* callback)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == 0);
- info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text;
- info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ return NativeMessageBox::showOkCancelBox (iconType, title, message, associatedComponent, callback);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == 0);
+ info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text;
+ info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text;
- return info.invoke() != 0;
+ return info.invoke() != 0;
+ }
}
int AlertWindow::showYesNoCancelBox (AlertIconType iconType,
@@ -76990,14 +77031,38 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType,
Component* associatedComponent,
ModalComponentManager::Callback* callback)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == 0);
- info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text;
- info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text;
- info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ return NativeMessageBox::showYesNoCancelBox (iconType, title, message, associatedComponent, callback);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == 0);
+ info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text;
+ info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text;
+ info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text;
- return info.invoke();
+ return info.invoke();
+ }
}
+#if JUCE_MODAL_LOOPS_PERMITTED
+bool AlertWindow::showNativeDialogBox (const String& title,
+ const String& bodyText,
+ bool isOkCancel)
+{
+ if (isOkCancel)
+ {
+ return NativeMessageBox::showOkCancelBox (AlertWindow::NoIcon, title, bodyText);
+ }
+ else
+ {
+ NativeMessageBox::showMessageBox (AlertWindow::NoIcon, title, bodyText);
+ return true;
+ }
+}
+#endif
+
END_JUCE_NAMESPACE
/*** End of inlined file: juce_AlertWindow.cpp ***/
@@ -247614,19 +247679,21 @@ private:
return;
}
- if (LOWORD (wParam) == WA_CLICKACTIVE && component->isCurrentlyBlockedByAnotherModalComponent())
- {
- Component* const underMouse = component->getComponentAt (component->getMouseXYRelative());
+ Component* underMouse = component->getComponentAt (component->getMouseXYRelative());
- if (underMouse != 0 && underMouse->isCurrentlyBlockedByAnotherModalComponent())
+ if (underMouse == 0)
+ underMouse = component;
+
+ if (underMouse->isCurrentlyBlockedByAnotherModalComponent())
+ {
+ if (LOWORD (wParam) == WA_CLICKACTIVE)
Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
+ else
+ ModalComponentManager::getInstance()->bringModalComponentsToFront();
}
else
{
handleBroughtToFront();
-
- if (component->isCurrentlyBlockedByAnotherModalComponent())
- Component::getCurrentlyModalComponent()->toFront (true);
}
}
@@ -248480,13 +248547,107 @@ bool Process::isForegroundProcess()
return false;
}
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+class Win32MessageBox : public AsyncUpdater
{
- return MessageBox (0, bodyText.toWideCharPointer(), title.toWideCharPointer(),
- MB_SETFOREGROUND | (isOkCancel ? MB_OKCANCEL
- : MB_OK)) == IDOK;
+public:
+ Win32MessageBox (AlertWindow::AlertIconType iconType,
+ const String& title_, const String& message_,
+ Component* associatedComponent,
+ UINT extraFlags,
+ ModalComponentManager::Callback* callback_,
+ const bool runAsync)
+ : flags (extraFlags | getMessageBoxFlags (iconType)),
+ owner (getWindowForMessageBox (associatedComponent)),
+ title (title_), message (message_), callback (callback_)
+ {
+ if (runAsync)
+ triggerAsyncUpdate();
+ }
+
+ int getResult() const
+ {
+ const int r = MessageBox (owner, message.toWideCharPointer(), title.toWideCharPointer(), flags);
+ return (r == IDYES || r == IDOK) ? 1 : (r == IDNO ? 2 : 0);
+ }
+
+ void handleAsyncUpdate()
+ {
+ const int result = getResult();
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
+
+ delete this;
+ }
+
+private:
+ UINT flags;
+ HWND owner;
+ String title, message;
+ ModalComponentManager::Callback* callback;
+
+ static UINT getMessageBoxFlags (AlertWindow::AlertIconType iconType) throw()
+ {
+ UINT flags = MB_TASKMODAL | MB_SETFOREGROUND;
+
+ switch (iconType)
+ {
+ case AlertWindow::QuestionIcon: flags |= MB_ICONQUESTION; break;
+ case AlertWindow::WarningIcon: flags |= MB_ICONWARNING; break;
+ case AlertWindow::InfoIcon: flags |= MB_ICONINFORMATION; break;
+ default: break;
+ }
+
+ return flags;
+ }
+
+ static HWND getWindowForMessageBox (Component* associatedComponent)
+ {
+ return associatedComponent != 0 ? (HWND) associatedComponent->getWindowHandle() : 0;
+ }
+};
+
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ Win32MessageBox box (iconType, title, message, associatedComponent, MB_OK, 0, false);
+ (void) box.getResult();
+}
+
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ new Win32MessageBox (iconType, title, message, associatedComponent, MB_OK, 0, true);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new Win32MessageBox (iconType, title, message, associatedComponent,
+ MB_OKCANCEL, callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult() != 0;
+
+ mb.release();
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new Win32MessageBox (iconType, title, message, associatedComponent,
+ MB_YESNOCANCEL, callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult();
+
+ mb.release();
+ return 0;
}
void Desktop::createMouseInputSources()
@@ -264775,17 +264936,37 @@ void PlatformUtilities::beep()
std::cout << "\a" << std::flush;
}
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
- // use a non-native one for the time being..
- if (isOkCancel)
- return AlertWindow::showOkCancelBox (AlertWindow::NoIcon, title, bodyText);
- else
- AlertWindow::showMessageBox (AlertWindow::NoIcon, title, bodyText);
+ AlertWindow::showMessageBox (AlertWindow::NoIcon, title, message);
+}
- return true;
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ AlertWindow::showMessageBoxAsync (AlertWindow::NoIcon, title, message);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ return AlertWindow::showOkCancelBox (iconType, title, message, String::empty, String::empty,
+ associatedComponent, callback);
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ return AlertWindow::showYesNoCancelBox (iconType, title, message,
+ String::empty, String::empty, String::empty,
+ associatedComponent, callback);
}
const int KeyPress::spaceKey = XK_space & 0xff;
@@ -269504,65 +269685,137 @@ void PlatformUtilities::addItemToDock (const File& file)
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
+class iOSMessageBox;
+
END_JUCE_NAMESPACE
@interface JuceAlertBoxDelegate : NSObject
{
@public
- bool clickedOk;
+ iOSMessageBox* owner;
}
- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex;
@end
+BEGIN_JUCE_NAMESPACE
+
+class iOSMessageBox
+{
+public:
+ iOSMessageBox (const String& title, const String& message,
+ NSString* button1, NSString* button2, NSString* button3,
+ ModalComponentManager::Callback* callback_, const bool isAsync_)
+ : result (0), delegate (nil), alert (nil),
+ callback (callback_), isYesNo (button3 != nil), isAsync (isAsync_)
+ {
+ delegate = [[JuceAlertBoxDelegate alloc] init];
+ delegate->owner = this;
+
+ alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title)
+ message: juceStringToNS (message)
+ delegate: delegate
+ cancelButtonTitle: button1
+ otherButtonTitles: button2, button3, nil];
+ [alert retain];
+ [alert show];
+ }
+
+ ~iOSMessageBox()
+ {
+ [alert release];
+ [delegate release];
+ }
+
+ int getResult()
+ {
+ jassert (callback == 0);
+ const ScopedAutoReleasePool pool;
+
+ while (! alert.hidden && alert.superview != nil)
+ [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
+
+ return result;
+ }
+
+ void buttonClicked (const int buttonIndex) throw()
+ {
+ result = buttonIndex;
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
+
+ if (isAsync)
+ delete this;
+ }
+
+private:
+ int result;
+ JuceAlertBoxDelegate* delegate;
+ UIAlertView* alert;
+ ModalComponentManager::Callback* callback;
+ const bool isYesNo, isAsync;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox);
+};
+
+END_JUCE_NAMESPACE
+
@implementation JuceAlertBoxDelegate
- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex
{
- clickedOk = (buttonIndex == 0);
+ owner->buttonClicked (buttonIndex);
alertView.hidden = true;
}
@end
-
BEGIN_JUCE_NAMESPACE
-// (This function is used directly by other bits of code)
-bool juce_iPhoneShowModalAlert (const String& title,
- const String& bodyText,
- NSString* okButtonText,
- NSString* cancelButtonText)
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
const ScopedAutoReleasePool pool;
-
- JuceAlertBoxDelegate* callback = [[JuceAlertBoxDelegate alloc] init];
-
- UIAlertView* alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title)
- message: juceStringToNS (bodyText)
- delegate: callback
- cancelButtonTitle: okButtonText
- otherButtonTitles: cancelButtonText, nil];
- [alert retain];
- [alert show];
-
- while (! alert.hidden && alert.superview != nil)
- [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
-
- const bool result = callback->clickedOk;
- [alert release];
- [callback release];
-
- return result;
+ iOSMessageBox mb (title, message, @"OK", nil, nil, 0, false);
+ (void) mb.getResult();
}
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
- return juce_iPhoneShowModalAlert (title, bodyText,
- @"OK",
- isOkCancel ? @"Cancel" : nil);
+ const ScopedAutoReleasePool pool;
+ new iOSMessageBox (title, message, @"OK", nil, nil, 0, true);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new iOSMessageBox (title, message, @"Cancel", @"OK", nil, callback, callback != 0));
+
+ if (callback == 0)
+ return mb->getResult() == 1;
+
+ mb.release();
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new iOSMessageBox (title, message, @"Cancel", @"Yes", @"No", callback, callback != 0));
+
+ if (callback == 0)
+ return mb->getResult();
+
+ mb.release();
+ return 0;
}
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles)
@@ -269630,16 +269883,111 @@ void PlatformUtilities::addItemToDock (const File& file)
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+class OSXMessageBox : public AsyncUpdater
{
- const ScopedAutoReleasePool pool;
- return NSRunAlertPanel (juceStringToNS (title),
- juceStringToNS (bodyText),
- @"Ok",
- isOkCancel ? @"Cancel" : nil,
- nil) == NSAlertDefaultReturn;
+public:
+ OSXMessageBox (AlertWindow::AlertIconType iconType_,
+ const String& title_, const String& message_,
+ NSString* button1_, NSString* button2_, NSString* button3_,
+ ModalComponentManager::Callback* callback_,
+ const bool runAsync)
+ : iconType (iconType_), title (title_),
+ message (message_), callback (callback_),
+ button1 ([button1_ retain]),
+ button2 ([button2_ retain]),
+ button3 ([button3_ retain])
+ {
+ if (runAsync)
+ triggerAsyncUpdate();
+ }
+
+ ~OSXMessageBox()
+ {
+ [button1 release];
+ [button2 release];
+ [button3 release];
+ }
+
+ int getResult() const
+ {
+ const ScopedAutoReleasePool pool;
+ NSInteger r = getRawResult();
+ return r == NSAlertDefaultReturn ? 1 : (r == NSAlertOtherReturn ? 2 : 0);
+ }
+
+ void handleAsyncUpdate()
+ {
+ const int result = getResult();
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
+
+ delete this;
+ }
+
+private:
+ AlertWindow::AlertIconType iconType;
+ String title, message;
+ ModalComponentManager::Callback* callback;
+ NSString* button1;
+ NSString* button2;
+ NSString* button3;
+
+ NSInteger getRawResult() const
+ {
+ NSString* messageString = juceStringToNS (message);
+ NSString* titleString = juceStringToNS (title);
+
+ switch (iconType)
+ {
+ case AlertWindow::InfoIcon: return NSRunInformationalAlertPanel (titleString, messageString, button1, button2, button3);
+ case AlertWindow::WarningIcon: return NSRunCriticalAlertPanel (titleString, messageString, button1, button2, button3);
+ default: return NSRunAlertPanel (titleString, messageString, button1, button2, button3);
+ }
+ }
+};
+
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ OSXMessageBox box (iconType, title, message, @"OK", nil, nil, 0, false);
+ (void) box.getResult();
+}
+
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ new OSXMessageBox (iconType, title, message, @"OK", nil, nil, 0, true);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new OSXMessageBox (iconType, title, message,
+ @"OK", @"Cancel", nil, callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult() == 1;
+
+ mb.release();
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new OSXMessageBox (iconType, title, message,
+ @"Yes", @"Cancel", @"No", callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult();
+
+ mb.release();
+ return 0;
}
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool /*canMoveFiles*/)
@@ -283771,6 +284119,9 @@ BEGIN_JUCE_NAMESPACE
METHOD (activityClass, excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
METHOD (activityClass, renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
STATICMETHOD (activityClass, createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;ILjava/lang/StringBuffer;)Lcom/juce/JuceAppActivity$HTTPStream;") \
+ METHOD (activityClass, showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
+ METHOD (activityClass, showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
+ METHOD (activityClass, showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
\
METHOD (stringBufferClass, stringBufferConstructor, "", "()V") \
METHOD (stringBufferClass, stringBufferToString, "toString", "()Ljava/lang/String;") \
@@ -287405,12 +287756,44 @@ bool Process::isForegroundProcess()
return true; // TODO
}
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
- // TODO
+ android.activity.callVoidMethod (android.showMessageBox, javaString (title).get(), javaString (message).get(), (jlong) 0);
+}
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ jassert (callback != 0); // on android, all alerts must be non-modal!!
+
+ android.activity.callVoidMethod (android.showOkCancelBox, javaString (title).get(), javaString (message).get(),
+ (jlong) (pointer_sized_int) callback);
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ jassert (callback != 0); // on android, all alerts must be non-modal!!
+
+ android.activity.callVoidMethod (android.showYesNoCancelBox, javaString (title).get(), javaString (message).get(),
+ (jlong) (pointer_sized_int) callback);
+ return 0;
+}
+
+JUCE_JNI_CALLBACK (JuceAppActivity, alertDismissed, void, (JNIEnv* env, jobject activity,
+ jlong callbackAsLong, jint result))
+{
+ ModalComponentManager::Callback* callback = (ModalComponentManager::Callback*) callbackAsLong;
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
}
void Desktop::setScreenSaverEnabled (const bool isEnabled)
diff --git a/juce_amalgamated.h b/juce_amalgamated.h
index 760f04f516..e993884691 100644
--- a/juce_amalgamated.h
+++ b/juce_amalgamated.h
@@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
-#define JUCE_BUILDNUMBER 60
+#define JUCE_BUILDNUMBER 61
/** Current Juce version number.
@@ -232,7 +232,7 @@ namespace JuceDummyNamespace {}
#endif
#endif
- #if ! JUCE_VC7_OR_EARLIER && ! defined (__INTEL_COMPILER)
+ #if ! JUCE_VC7_OR_EARLIER
#define JUCE_USE_INTRINSICS 1
#endif
#else
@@ -5478,7 +5478,7 @@ std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostre
}
/** Writes a string to an OutputStream as UTF8. */
-JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text);
+JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& stringToWrite);
#endif // __JUCE_STRING_JUCEHEADER__
/*** End of inlined file: juce_String.h ***/
@@ -29980,7 +29980,7 @@ public:
void attachCallback (Component* component, Callback* callback);
/** Brings any modal components to the front. */
- void bringModalComponentsToFront();
+ void bringModalComponentsToFront (bool topOneShouldGrabFocus = true);
#if JUCE_MODAL_LOOPS_PERMITTED
/** Runs the event loop until the currently topmost modal component is dismissed, and
@@ -57042,9 +57042,11 @@ public:
it'll show a box with just an ok button
@returns true if the ok button was pressed, false if they pressed cancel.
*/
+ #if JUCE_MODAL_LOOPS_PERMITTED
static bool JUCE_CALLTYPE showNativeDialogBox (const String& title,
const String& bodyText,
bool isOkCancel);
+ #endif
/** A set of colour IDs to use to change the colour of various aspects of the alert box.
@@ -60086,6 +60088,9 @@ public:
virtual const Font getAlertWindowMessageFont();
virtual const Font getAlertWindowFont();
+ void setUsingNativeAlertWindows (bool shouldUseNativeAlerts);
+ bool isUsingNativeAlertWindows();
+
/** Draws a progress bar.
If the progress value is less than 0 or greater than 1.0, this should draw a spinning
@@ -60511,6 +60516,8 @@ private:
ScopedPointer folderImage, documentImage;
+ bool useNativeAlertWindows;
+
void drawShinyButtonShape (Graphics& g,
float x, float y, float w, float h, float maxCornerSize,
const Colour& baseColour,
@@ -64605,6 +64612,145 @@ private:
#endif
#ifndef __JUCE_DOCUMENTWINDOW_JUCEHEADER__
+#endif
+#ifndef __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
+
+/*** Start of inlined file: juce_NativeMessageBox.h ***/
+#ifndef __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
+#define __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
+
+class NativeMessageBox
+{
+public:
+ /** Shows a dialog box that just has a message and a single 'ok' button to close it.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ */
+ #if JUCE_MODAL_LOOPS_PERMITTED
+ static void JUCE_CALLTYPE showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ Component* associatedComponent = 0);
+ #endif
+
+ /** Shows a dialog box that just has a message and a single 'ok' button to close it.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ */
+ static void JUCE_CALLTYPE showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ Component* associatedComponent = 0);
+
+ /** Shows a dialog box with two buttons.
+
+ Ideal for ok/cancel or yes/no choices. The return key can also be used
+ to trigger the first button, and the escape key for the second button.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ @param callback if this is non-null, the menu will be launched asynchronously,
+ returning immediately, and the callback will receive a call to its
+ modalStateFinished() when the box is dismissed, with its parameter
+ being 1 if the ok button was pressed, or 0 for cancel, The callback object
+ will be owned and deleted by the system, so make sure that it works
+ safely and doesn't keep any references to objects that might be deleted
+ before it gets called.
+ @returns true if button 1 was clicked, false if it was button 2. If the callback parameter
+ is not null, the method always returns false, and the user's choice is delivered
+ later by the callback.
+ */
+ static bool JUCE_CALLTYPE showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ #if JUCE_MODAL_LOOPS_PERMITTED
+ Component* associatedComponent = 0,
+ ModalComponentManager::Callback* callback = 0);
+ #else
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback);
+ #endif
+
+ /** Shows a dialog box with three buttons.
+
+ Ideal for yes/no/cancel boxes.
+
+ The escape key can be used to trigger the third button.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ @param callback if this is non-null, the menu will be launched asynchronously,
+ returning immediately, and the callback will receive a call to its
+ modalStateFinished() when the box is dismissed, with its parameter
+ being 1 if the "yes" button was pressed, 2 for the "no" button, or 0
+ if it was cancelled, The callback object will be owned and deleted by the
+ system, so make sure that it works safely and doesn't keep any references
+ to objects that might be deleted before it gets called.
+
+ @returns If the callback parameter has been set, this returns 0. Otherwise, it returns one
+ of the following values:
+ - 0 if 'cancel' was pressed
+ - 1 if 'yes' was pressed
+ - 2 if 'no' was pressed
+ */
+ static int JUCE_CALLTYPE showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ #if JUCE_MODAL_LOOPS_PERMITTED
+ Component* associatedComponent = 0,
+ ModalComponentManager::Callback* callback = 0);
+ #else
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback);
+ #endif
+};
+
+#endif // __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
+/*** End of inlined file: juce_NativeMessageBox.h ***/
+
+
#endif
#ifndef __JUCE_RESIZABLEWINDOW_JUCEHEADER__
diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h
index 536e64804f..5a5d14586c 100644
--- a/src/core/juce_StandardHeader.h
+++ b/src/core/juce_StandardHeader.h
@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
-#define JUCE_BUILDNUMBER 60
+#define JUCE_BUILDNUMBER 61
/** Current Juce version number.
diff --git a/src/core/juce_TargetPlatform.h b/src/core/juce_TargetPlatform.h
index 49c91e956a..6e118db8e4 100644
--- a/src/core/juce_TargetPlatform.h
+++ b/src/core/juce_TargetPlatform.h
@@ -174,7 +174,7 @@
#endif
#endif
- #if ! JUCE_VC7_OR_EARLIER && ! defined (__INTEL_COMPILER)
+ #if ! JUCE_VC7_OR_EARLIER
#define JUCE_USE_INTRINSICS 1
#endif
#else
diff --git a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp
index f08583751f..311691aaac 100644
--- a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp
+++ b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp
@@ -163,6 +163,7 @@ namespace LookAndFeelHelpers
//==============================================================================
LookAndFeel::LookAndFeel()
+ : useNativeAlertWindows (false)
{
/* if this fails it means you're trying to create a LookAndFeel object before
the static Colours have been initialised. That ain't gonna work. It probably
@@ -696,6 +697,20 @@ const Font LookAndFeel::getAlertWindowFont()
return Font (12.0f);
}
+void LookAndFeel::setUsingNativeAlertWindows (bool shouldUseNativeAlerts)
+{
+ useNativeAlertWindows = shouldUseNativeAlerts;
+}
+
+bool LookAndFeel::isUsingNativeAlertWindows()
+{
+ #if JUCE_LINUX
+ return false; // not available currently..
+ #else
+ return useNativeAlertWindows;
+ #endif
+}
+
//==============================================================================
void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar,
int width, int height,
@@ -1688,7 +1703,6 @@ class SliderLabelComp : public Label
{
public:
SliderLabelComp() : Label (String::empty, String::empty) {}
- ~SliderLabelComp() {}
void mouseWheelMove (const MouseEvent&, float, float) {}
};
diff --git a/src/gui/components/lookandfeel/juce_LookAndFeel.h b/src/gui/components/lookandfeel/juce_LookAndFeel.h
index 0e6270b544..f38c1b8a2d 100644
--- a/src/gui/components/lookandfeel/juce_LookAndFeel.h
+++ b/src/gui/components/lookandfeel/juce_LookAndFeel.h
@@ -203,6 +203,9 @@ public:
virtual const Font getAlertWindowMessageFont();
virtual const Font getAlertWindowFont();
+ void setUsingNativeAlertWindows (bool shouldUseNativeAlerts);
+ bool isUsingNativeAlertWindows();
+
/** Draws a progress bar.
If the progress value is less than 0 or greater than 1.0, this should draw a spinning
@@ -657,6 +660,7 @@ private:
ScopedPointer folderImage, documentImage;
+ bool useNativeAlertWindows;
void drawShinyButtonShape (Graphics& g,
float x, float y, float w, float h, float maxCornerSize,
diff --git a/src/gui/components/windows/juce_AlertWindow.cpp b/src/gui/components/windows/juce_AlertWindow.cpp
index 7365ce6274..2a2ec63f2c 100644
--- a/src/gui/components/windows/juce_AlertWindow.cpp
+++ b/src/gui/components/windows/juce_AlertWindow.cpp
@@ -35,6 +35,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../../events/juce_MessageManager.h"
#include "../../../application/juce_Application.h"
#include "../../../memory/juce_ScopedPointer.h"
+#include "juce_NativeMessageBox.h"
//==============================================================================
@@ -668,10 +669,17 @@ void AlertWindow::showMessageBox (AlertIconType iconType,
const String& buttonText,
Component* associatedComponent)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, true);
- info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ NativeMessageBox::showMessageBox (iconType, title, message, associatedComponent);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, true);
+ info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
- info.invoke();
+ info.invoke();
+ }
}
#endif
@@ -681,10 +689,17 @@ void AlertWindow::showMessageBoxAsync (AlertIconType iconType,
const String& buttonText,
Component* associatedComponent)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, false);
- info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ return NativeMessageBox::showMessageBoxAsync (iconType, title, message, associatedComponent);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 1, 0, false);
+ info.button1 = buttonText.isEmpty() ? TRANS("ok") : buttonText;
- info.invoke();
+ info.invoke();
+ }
}
bool AlertWindow::showOkCancelBox (AlertIconType iconType,
@@ -695,11 +710,18 @@ bool AlertWindow::showOkCancelBox (AlertIconType iconType,
Component* associatedComponent,
ModalComponentManager::Callback* callback)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == 0);
- info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text;
- info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ return NativeMessageBox::showOkCancelBox (iconType, title, message, associatedComponent, callback);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == 0);
+ info.button1 = button1Text.isEmpty() ? TRANS("ok") : button1Text;
+ info.button2 = button2Text.isEmpty() ? TRANS("cancel") : button2Text;
- return info.invoke() != 0;
+ return info.invoke() != 0;
+ }
}
int AlertWindow::showYesNoCancelBox (AlertIconType iconType,
@@ -711,12 +733,36 @@ int AlertWindow::showYesNoCancelBox (AlertIconType iconType,
Component* associatedComponent,
ModalComponentManager::Callback* callback)
{
- AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == 0);
- info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text;
- info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text;
- info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text;
+ if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
+ {
+ return NativeMessageBox::showYesNoCancelBox (iconType, title, message, associatedComponent, callback);
+ }
+ else
+ {
+ AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == 0);
+ info.button1 = button1Text.isEmpty() ? TRANS("yes") : button1Text;
+ info.button2 = button2Text.isEmpty() ? TRANS("no") : button2Text;
+ info.button3 = button3Text.isEmpty() ? TRANS("cancel") : button3Text;
- return info.invoke();
+ return info.invoke();
+ }
}
+#if JUCE_MODAL_LOOPS_PERMITTED
+bool AlertWindow::showNativeDialogBox (const String& title,
+ const String& bodyText,
+ bool isOkCancel)
+{
+ if (isOkCancel)
+ {
+ return NativeMessageBox::showOkCancelBox (AlertWindow::NoIcon, title, bodyText);
+ }
+ else
+ {
+ NativeMessageBox::showMessageBox (AlertWindow::NoIcon, title, bodyText);
+ return true;
+ }
+}
+#endif
+
END_JUCE_NAMESPACE
diff --git a/src/gui/components/windows/juce_AlertWindow.h b/src/gui/components/windows/juce_AlertWindow.h
index c832fee275..9325718478 100644
--- a/src/gui/components/windows/juce_AlertWindow.h
+++ b/src/gui/components/windows/juce_AlertWindow.h
@@ -395,9 +395,11 @@ public:
it'll show a box with just an ok button
@returns true if the ok button was pressed, false if they pressed cancel.
*/
+ #if JUCE_MODAL_LOOPS_PERMITTED
static bool JUCE_CALLTYPE showNativeDialogBox (const String& title,
const String& bodyText,
bool isOkCancel);
+ #endif
//==============================================================================
diff --git a/src/gui/components/windows/juce_NativeMessageBox.h b/src/gui/components/windows/juce_NativeMessageBox.h
new file mode 100644
index 0000000000..3e6f0bf616
--- /dev/null
+++ b/src/gui/components/windows/juce_NativeMessageBox.h
@@ -0,0 +1,157 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-11 by Raw Material Software Ltd.
+
+ ------------------------------------------------------------------------------
+
+ JUCE can be redistributed and/or modified under the terms of the GNU General
+ Public License (Version 2), as published by the Free Software Foundation.
+ A copy of the license is included in the JUCE distribution, or can be found
+ online at www.gnu.org/licenses.
+
+ 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.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.rawmaterialsoftware.com/juce for more information.
+
+ ==============================================================================
+*/
+
+#ifndef __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
+#define __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
+
+class NativeMessageBox
+{
+public:
+ /** Shows a dialog box that just has a message and a single 'ok' button to close it.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ */
+ #if JUCE_MODAL_LOOPS_PERMITTED
+ static void JUCE_CALLTYPE showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ Component* associatedComponent = 0);
+ #endif
+
+ /** Shows a dialog box that just has a message and a single 'ok' button to close it.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ */
+ static void JUCE_CALLTYPE showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ Component* associatedComponent = 0);
+
+ /** Shows a dialog box with two buttons.
+
+ Ideal for ok/cancel or yes/no choices. The return key can also be used
+ to trigger the first button, and the escape key for the second button.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ @param callback if this is non-null, the menu will be launched asynchronously,
+ returning immediately, and the callback will receive a call to its
+ modalStateFinished() when the box is dismissed, with its parameter
+ being 1 if the ok button was pressed, or 0 for cancel, The callback object
+ will be owned and deleted by the system, so make sure that it works
+ safely and doesn't keep any references to objects that might be deleted
+ before it gets called.
+ @returns true if button 1 was clicked, false if it was button 2. If the callback parameter
+ is not null, the method always returns false, and the user's choice is delivered
+ later by the callback.
+ */
+ static bool JUCE_CALLTYPE showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ #if JUCE_MODAL_LOOPS_PERMITTED
+ Component* associatedComponent = 0,
+ ModalComponentManager::Callback* callback = 0);
+ #else
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback);
+ #endif
+
+ /** Shows a dialog box with three buttons.
+
+ Ideal for yes/no/cancel boxes.
+
+ The escape key can be used to trigger the third button.
+
+ If the callback parameter is null, the box is shown modally, and the method will
+ block until the user has clicked the button (or pressed the escape or return keys).
+ If the callback parameter is non-null, the box will be displayed and placed into a
+ modal state, but this method will return immediately, and the callback will be invoked
+ later when the user dismisses the box.
+
+ @param iconType the type of icon to show
+ @param title the headline to show at the top of the box
+ @param message a longer, more descriptive message to show underneath the title
+ @param associatedComponent if this is non-null, it specifies the component that the
+ alert window should be associated with. Depending on the look
+ and feel, this might be used for positioning of the alert window.
+ @param callback if this is non-null, the menu will be launched asynchronously,
+ returning immediately, and the callback will receive a call to its
+ modalStateFinished() when the box is dismissed, with its parameter
+ being 1 if the "yes" button was pressed, 2 for the "no" button, or 0
+ if it was cancelled, The callback object will be owned and deleted by the
+ system, so make sure that it works safely and doesn't keep any references
+ to objects that might be deleted before it gets called.
+
+ @returns If the callback parameter has been set, this returns 0. Otherwise, it returns one
+ of the following values:
+ - 0 if 'cancel' was pressed
+ - 1 if 'yes' was pressed
+ - 2 if 'no' was pressed
+ */
+ static int JUCE_CALLTYPE showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title,
+ const String& message,
+ #if JUCE_MODAL_LOOPS_PERMITTED
+ Component* associatedComponent = 0,
+ ModalComponentManager::Callback* callback = 0);
+ #else
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback);
+ #endif
+};
+
+#endif // __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h
index 547280f0d3..45d3b4f434 100644
--- a/src/juce_app_includes.h
+++ b/src/juce_app_includes.h
@@ -614,6 +614,9 @@
#ifndef __JUCE_DOCUMENTWINDOW_JUCEHEADER__
#include "gui/components/windows/juce_DocumentWindow.h"
#endif
+#ifndef __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__
+ #include "gui/components/windows/juce_NativeMessageBox.h"
+#endif
#ifndef __JUCE_RESIZABLEWINDOW_JUCEHEADER__
#include "gui/components/windows/juce_ResizableWindow.h"
#endif
diff --git a/src/native/android/java/JuceAppActivity.java b/src/native/android/java/JuceAppActivity.java
index 617f67a97c..950590859c 100644
--- a/src/native/android/java/JuceAppActivity.java
+++ b/src/native/android/java/JuceAppActivity.java
@@ -26,6 +26,8 @@
package com.juce;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.os.Bundle;
import android.content.Context;
import android.view.ViewGroup;
@@ -167,6 +169,87 @@ public final class JuceAppActivity extends Activity
clipboard.setText (newText);
}
+ //==============================================================================
+ public final void showMessageBox (String title, String message, final long callback)
+ {
+ AlertDialog.Builder builder = new AlertDialog.Builder (this);
+ builder.setTitle (title)
+ .setMessage (message)
+ .setCancelable (true)
+ .setPositiveButton ("OK", new DialogInterface.OnClickListener()
+ {
+ public void onClick (DialogInterface dialog, int id)
+ {
+ dialog.cancel();
+ JuceAppActivity.this.alertDismissed (callback, 0);
+ }
+ });
+
+ builder.create().show();
+ }
+
+ public final void showOkCancelBox (String title, String message, final long callback)
+ {
+ AlertDialog.Builder builder = new AlertDialog.Builder (this);
+ builder.setTitle (title)
+ .setMessage (message)
+ .setCancelable (true)
+ .setPositiveButton ("OK", new DialogInterface.OnClickListener()
+ {
+ public void onClick (DialogInterface dialog, int id)
+ {
+ dialog.cancel();
+ JuceAppActivity.this.alertDismissed (callback, 1);
+ }
+ })
+ .setNegativeButton ("Cancel", new DialogInterface.OnClickListener()
+ {
+ public void onClick (DialogInterface dialog, int id)
+ {
+ dialog.cancel();
+ JuceAppActivity.this.alertDismissed (callback, 0);
+ }
+ });
+
+ builder.create().show();
+ }
+
+ public final void showYesNoCancelBox (String title, String message, final long callback)
+ {
+ AlertDialog.Builder builder = new AlertDialog.Builder (this);
+ builder.setTitle (title)
+ .setMessage (message)
+ .setCancelable (true)
+ .setPositiveButton ("Yes", new DialogInterface.OnClickListener()
+ {
+ public void onClick (DialogInterface dialog, int id)
+ {
+ dialog.cancel();
+ JuceAppActivity.this.alertDismissed (callback, 1);
+ }
+ })
+ .setNegativeButton ("No", new DialogInterface.OnClickListener()
+ {
+ public void onClick (DialogInterface dialog, int id)
+ {
+ dialog.cancel();
+ JuceAppActivity.this.alertDismissed (callback, 2);
+ }
+ })
+ .setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
+ {
+ public void onClick (DialogInterface dialog, int id)
+ {
+ dialog.cancel();
+ JuceAppActivity.this.alertDismissed (callback, 0);
+ }
+ });
+
+ builder.create().show();
+ }
+
+ public native void alertDismissed (long callback, int id);
+
//==============================================================================
public final int[] renderGlyph (char glyph, Paint paint, Matrix matrix, Rect bounds)
{
diff --git a/src/native/android/juce_android_NativeCode.cpp b/src/native/android/juce_android_NativeCode.cpp
index 8623aa56e2..f25376f9c0 100644
--- a/src/native/android/juce_android_NativeCode.cpp
+++ b/src/native/android/juce_android_NativeCode.cpp
@@ -66,6 +66,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../gui/graphics/imaging/juce_CameraDevice.h"
#include "../../gui/components/windows/juce_ComponentPeer.h"
#include "../../gui/components/windows/juce_AlertWindow.h"
+#include "../../gui/components/windows/juce_NativeMessageBox.h"
#include "../../gui/components/juce_Desktop.h"
#include "../../gui/components/menus/juce_MenuBarModel.h"
#include "../../gui/components/special/juce_OpenGLComponent.h"
@@ -147,6 +148,9 @@ BEGIN_JUCE_NAMESPACE
METHOD (activityClass, excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
METHOD (activityClass, renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
STATICMETHOD (activityClass, createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;ILjava/lang/StringBuffer;)Lcom/juce/JuceAppActivity$HTTPStream;") \
+ METHOD (activityClass, showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
+ METHOD (activityClass, showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
+ METHOD (activityClass, showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
\
METHOD (stringBufferClass, stringBufferConstructor, "", "()V") \
METHOD (stringBufferClass, stringBufferToString, "toString", "()Ljava/lang/String;") \
diff --git a/src/native/android/juce_android_Windowing.cpp b/src/native/android/juce_android_Windowing.cpp
index 760a81993b..9d3d4c74d5 100644
--- a/src/native/android/juce_android_Windowing.cpp
+++ b/src/native/android/juce_android_Windowing.cpp
@@ -592,12 +592,44 @@ bool Process::isForegroundProcess()
}
//==============================================================================
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
- // TODO
+ android.activity.callVoidMethod (android.showMessageBox, javaString (title).get(), javaString (message).get(), (jlong) 0);
+}
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ jassert (callback != 0); // on android, all alerts must be non-modal!!
+
+ android.activity.callVoidMethod (android.showOkCancelBox, javaString (title).get(), javaString (message).get(),
+ (jlong) (pointer_sized_int) callback);
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ jassert (callback != 0); // on android, all alerts must be non-modal!!
+
+ android.activity.callVoidMethod (android.showYesNoCancelBox, javaString (title).get(), javaString (message).get(),
+ (jlong) (pointer_sized_int) callback);
+ return 0;
+}
+
+JUCE_JNI_CALLBACK (JuceAppActivity, alertDismissed, void, (JNIEnv* env, jobject activity,
+ jlong callbackAsLong, jint result))
+{
+ ModalComponentManager::Callback* callback = (ModalComponentManager::Callback*) callbackAsLong;
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
}
//==============================================================================
diff --git a/src/native/linux/juce_linux_NativeCode.cpp b/src/native/linux/juce_linux_NativeCode.cpp
index b5dc1bcbc5..0fa3622f60 100644
--- a/src/native/linux/juce_linux_NativeCode.cpp
+++ b/src/native/linux/juce_linux_NativeCode.cpp
@@ -88,6 +88,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../gui/components/keyboard/juce_KeyPress.h"
#include "../../gui/components/windows/juce_ComponentPeer.h"
#include "../../gui/components/windows/juce_AlertWindow.h"
+#include "../../gui/components/windows/juce_NativeMessageBox.h"
#include "../../gui/components/filebrowser/juce_FileChooser.h"
#include "../../gui/components/special/juce_WebBrowserComponent.h"
#include "../../gui/components/special/juce_OpenGLComponent.h"
diff --git a/src/native/linux/juce_linux_Windowing.cpp b/src/native/linux/juce_linux_Windowing.cpp
index cf46c06399..7f9a19cc78 100644
--- a/src/native/linux/juce_linux_Windowing.cpp
+++ b/src/native/linux/juce_linux_Windowing.cpp
@@ -3342,19 +3342,40 @@ void PlatformUtilities::beep()
//==============================================================================
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
- // use a non-native one for the time being..
- if (isOkCancel)
- return AlertWindow::showOkCancelBox (AlertWindow::NoIcon, title, bodyText);
- else
- AlertWindow::showMessageBox (AlertWindow::NoIcon, title, bodyText);
-
- return true;
+ AlertWindow::showMessageBox (AlertWindow::NoIcon, title, message);
}
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ AlertWindow::showMessageBoxAsync (AlertWindow::NoIcon, title, message);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ return AlertWindow::showOkCancelBox (iconType, title, message, String::empty, String::empty,
+ associatedComponent, callback);
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ return AlertWindow::showYesNoCancelBox (iconType, title, message,
+ String::empty, String::empty, String::empty,
+ associatedComponent, callback);
+}
+
+
//==============================================================================
const int KeyPress::spaceKey = XK_space & 0xff;
const int KeyPress::returnKey = XK_Return & 0xff;
diff --git a/src/native/mac/juce_ios_MiscUtilities.mm b/src/native/mac/juce_ios_MiscUtilities.mm
index dce66ede00..9aefd69707 100644
--- a/src/native/mac/juce_ios_MiscUtilities.mm
+++ b/src/native/mac/juce_ios_MiscUtilities.mm
@@ -91,65 +91,140 @@ void PlatformUtilities::addItemToDock (const File& file)
//==============================================================================
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
+//==============================================================================
+class iOSMessageBox;
+
END_JUCE_NAMESPACE
@interface JuceAlertBoxDelegate : NSObject
{
@public
- bool clickedOk;
+ iOSMessageBox* owner;
}
- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex;
@end
+BEGIN_JUCE_NAMESPACE
+
+class iOSMessageBox
+{
+public:
+ iOSMessageBox (const String& title, const String& message,
+ NSString* button1, NSString* button2, NSString* button3,
+ ModalComponentManager::Callback* callback_, const bool isAsync_)
+ : result (0), delegate (nil), alert (nil),
+ callback (callback_), isYesNo (button3 != nil), isAsync (isAsync_)
+ {
+ delegate = [[JuceAlertBoxDelegate alloc] init];
+ delegate->owner = this;
+
+ alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title)
+ message: juceStringToNS (message)
+ delegate: delegate
+ cancelButtonTitle: button1
+ otherButtonTitles: button2, button3, nil];
+ [alert retain];
+ [alert show];
+ }
+
+ ~iOSMessageBox()
+ {
+ [alert release];
+ [delegate release];
+ }
+
+ int getResult()
+ {
+ jassert (callback == 0);
+ const ScopedAutoReleasePool pool;
+
+ while (! alert.hidden && alert.superview != nil)
+ [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
+
+ return result;
+ }
+
+ void buttonClicked (const int buttonIndex) throw()
+ {
+ result = buttonIndex;
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
+
+ if (isAsync)
+ delete this;
+ }
+
+private:
+ int result;
+ JuceAlertBoxDelegate* delegate;
+ UIAlertView* alert;
+ ModalComponentManager::Callback* callback;
+ const bool isYesNo, isAsync;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox);
+};
+
+END_JUCE_NAMESPACE
+
@implementation JuceAlertBoxDelegate
- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex
{
- clickedOk = (buttonIndex == 0);
+ owner->buttonClicked (buttonIndex);
alertView.hidden = true;
}
@end
-
BEGIN_JUCE_NAMESPACE
-// (This function is used directly by other bits of code)
-bool juce_iPhoneShowModalAlert (const String& title,
- const String& bodyText,
- NSString* okButtonText,
- NSString* cancelButtonText)
+
+//==============================================================================
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
const ScopedAutoReleasePool pool;
-
- JuceAlertBoxDelegate* callback = [[JuceAlertBoxDelegate alloc] init];
-
- UIAlertView* alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title)
- message: juceStringToNS (bodyText)
- delegate: callback
- cancelButtonTitle: okButtonText
- otherButtonTitles: cancelButtonText, nil];
- [alert retain];
- [alert show];
-
- while (! alert.hidden && alert.superview != nil)
- [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
-
- const bool result = callback->clickedOk;
- [alert release];
- [callback release];
-
- return result;
+ iOSMessageBox mb (title, message, @"OK", nil, nil, 0, false);
+ (void) mb.getResult();
}
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
{
- return juce_iPhoneShowModalAlert (title, bodyText,
- @"OK",
- isOkCancel ? @"Cancel" : nil);
+ const ScopedAutoReleasePool pool;
+ new iOSMessageBox (title, message, @"OK", nil, nil, 0, true);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new iOSMessageBox (title, message, @"Cancel", @"OK", nil, callback, callback != 0));
+
+ if (callback == 0)
+ return mb->getResult() == 1;
+
+ mb.release();
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new iOSMessageBox (title, message, @"Cancel", @"Yes", @"No", callback, callback != 0));
+
+ if (callback == 0)
+ return mb->getResult();
+
+ mb.release();
+ return 0;
}
//==============================================================================
diff --git a/src/native/mac/juce_mac_MiscUtilities.mm b/src/native/mac/juce_mac_MiscUtilities.mm
index 0e630ebe50..5de106dea9 100644
--- a/src/native/mac/juce_mac_MiscUtilities.mm
+++ b/src/native/mac/juce_mac_MiscUtilities.mm
@@ -63,18 +63,116 @@ void PlatformUtilities::addItemToDock (const File& file)
//==============================================================================
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
+//==============================================================================
+class OSXMessageBox : public AsyncUpdater
{
- const ScopedAutoReleasePool pool;
- return NSRunAlertPanel (juceStringToNS (title),
- juceStringToNS (bodyText),
- @"Ok",
- isOkCancel ? @"Cancel" : nil,
- nil) == NSAlertDefaultReturn;
+public:
+ OSXMessageBox (AlertWindow::AlertIconType iconType_,
+ const String& title_, const String& message_,
+ NSString* button1_, NSString* button2_, NSString* button3_,
+ ModalComponentManager::Callback* callback_,
+ const bool runAsync)
+ : iconType (iconType_), title (title_),
+ message (message_), callback (callback_),
+ button1 ([button1_ retain]),
+ button2 ([button2_ retain]),
+ button3 ([button3_ retain])
+ {
+ if (runAsync)
+ triggerAsyncUpdate();
+ }
+
+ ~OSXMessageBox()
+ {
+ [button1 release];
+ [button2 release];
+ [button3 release];
+ }
+
+ int getResult() const
+ {
+ const ScopedAutoReleasePool pool;
+ NSInteger r = getRawResult();
+ return r == NSAlertDefaultReturn ? 1 : (r == NSAlertOtherReturn ? 2 : 0);
+ }
+
+ void handleAsyncUpdate()
+ {
+ const int result = getResult();
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
+
+ delete this;
+ }
+
+private:
+ AlertWindow::AlertIconType iconType;
+ String title, message;
+ ModalComponentManager::Callback* callback;
+ NSString* button1;
+ NSString* button2;
+ NSString* button3;
+
+ NSInteger getRawResult() const
+ {
+ NSString* messageString = juceStringToNS (message);
+ NSString* titleString = juceStringToNS (title);
+
+ switch (iconType)
+ {
+ case AlertWindow::InfoIcon: return NSRunInformationalAlertPanel (titleString, messageString, button1, button2, button3);
+ case AlertWindow::WarningIcon: return NSRunCriticalAlertPanel (titleString, messageString, button1, button2, button3);
+ default: return NSRunAlertPanel (titleString, messageString, button1, button2, button3);
+ }
+ }
+};
+
+
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ OSXMessageBox box (iconType, title, message, @"OK", nil, nil, 0, false);
+ (void) box.getResult();
}
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ new OSXMessageBox (iconType, title, message, @"OK", nil, nil, 0, true);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new OSXMessageBox (iconType, title, message,
+ @"OK", @"Cancel", nil, callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult() == 1;
+
+ mb.release();
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new OSXMessageBox (iconType, title, message,
+ @"Yes", @"Cancel", @"No", callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult();
+
+ mb.release();
+ return 0;
+}
+
+
//==============================================================================
bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool /*canMoveFiles*/)
{
diff --git a/src/native/mac/juce_mac_NativeCode.mm b/src/native/mac/juce_mac_NativeCode.mm
index d2ebc37077..625c4cd59d 100644
--- a/src/native/mac/juce_mac_NativeCode.mm
+++ b/src/native/mac/juce_mac_NativeCode.mm
@@ -71,6 +71,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../gui/graphics/imaging/juce_ImageFileFormat.h"
#include "../../gui/graphics/imaging/juce_CameraDevice.h"
#include "../../gui/components/windows/juce_AlertWindow.h"
+#include "../../gui/components/windows/juce_NativeMessageBox.h"
#include "../../gui/components/windows/juce_ComponentPeer.h"
#include "../../gui/components/juce_Desktop.h"
#include "../../gui/components/menus/juce_MenuBarModel.h"
diff --git a/src/native/windows/juce_win32_NativeCode.cpp b/src/native/windows/juce_win32_NativeCode.cpp
index f11bbb5b2c..9a098d5658 100644
--- a/src/native/windows/juce_win32_NativeCode.cpp
+++ b/src/native/windows/juce_win32_NativeCode.cpp
@@ -66,6 +66,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../gui/graphics/imaging/juce_CameraDevice.h"
#include "../../gui/components/windows/juce_ComponentPeer.h"
#include "../../gui/components/windows/juce_AlertWindow.h"
+#include "../../gui/components/windows/juce_NativeMessageBox.h"
#include "../../gui/components/juce_Desktop.h"
#include "../../gui/components/menus/juce_MenuBarModel.h"
#include "../../gui/components/special/juce_OpenGLComponent.h"
diff --git a/src/native/windows/juce_win32_Windowing.cpp b/src/native/windows/juce_win32_Windowing.cpp
index 7ae0c8d595..b3ca34645a 100644
--- a/src/native/windows/juce_win32_Windowing.cpp
+++ b/src/native/windows/juce_win32_Windowing.cpp
@@ -1,3227 +1,3320 @@
-/*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-11 by Raw Material Software Ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the GNU General
- Public License (Version 2), as published by the Free Software Foundation.
- A copy of the license is included in the JUCE distribution, or can be found
- online at www.gnu.org/licenses.
-
- 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.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.rawmaterialsoftware.com/juce for more information.
-
- ==============================================================================
-*/
-
-// (This file gets included by juce_win32_NativeCode.cpp, rather than being
-// compiled on its own).
-#if JUCE_INCLUDED_FILE
-
-
-//==============================================================================
-#undef GetSystemMetrics // multimon overrides this for some reason and causes a mess..
-
-// these are in the windows SDK, but need to be repeated here for GCC..
-#ifndef GET_APPCOMMAND_LPARAM
- #define FAPPCOMMAND_MASK 0xF000
- #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK))
- #define APPCOMMAND_MEDIA_NEXTTRACK 11
- #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
- #define APPCOMMAND_MEDIA_STOP 13
- #define APPCOMMAND_MEDIA_PLAY_PAUSE 14
- #define WM_APPCOMMAND 0x0319
-#endif
-
-extern void juce_repeatLastProcessPriority(); // in juce_win32_Threads.cpp
-extern void juce_CheckCurrentlyFocusedTopLevelWindow(); // in juce_TopLevelWindow.cpp
-extern bool juce_IsRunningInWine();
-
-#ifndef ULW_ALPHA
- #define ULW_ALPHA 0x00000002
-#endif
-
-#ifndef AC_SRC_ALPHA
- #define AC_SRC_ALPHA 0x01
-#endif
-
-static bool shouldDeactivateTitleBar = true;
-
-#define WM_TRAYNOTIFY WM_USER + 100
-
-//==============================================================================
-typedef BOOL (WINAPI* UpdateLayeredWinFunc) (HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);
-static UpdateLayeredWinFunc updateLayeredWindow = 0;
-
-bool Desktop::canUseSemiTransparentWindows() throw()
-{
- if (updateLayeredWindow == 0)
- {
- if (! juce_IsRunningInWine())
- {
- HMODULE user32Mod = GetModuleHandle (_T("user32.dll"));
- updateLayeredWindow = (UpdateLayeredWinFunc) GetProcAddress (user32Mod, "UpdateLayeredWindow");
- }
- }
-
- return updateLayeredWindow != 0;
-}
-
-Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
-{
- return upright;
-}
-
-//==============================================================================
-const int extendedKeyModifier = 0x10000;
-
-const int KeyPress::spaceKey = VK_SPACE;
-const int KeyPress::returnKey = VK_RETURN;
-const int KeyPress::escapeKey = VK_ESCAPE;
-const int KeyPress::backspaceKey = VK_BACK;
-const int KeyPress::deleteKey = VK_DELETE | extendedKeyModifier;
-const int KeyPress::insertKey = VK_INSERT | extendedKeyModifier;
-const int KeyPress::tabKey = VK_TAB;
-const int KeyPress::leftKey = VK_LEFT | extendedKeyModifier;
-const int KeyPress::rightKey = VK_RIGHT | extendedKeyModifier;
-const int KeyPress::upKey = VK_UP | extendedKeyModifier;
-const int KeyPress::downKey = VK_DOWN | extendedKeyModifier;
-const int KeyPress::homeKey = VK_HOME | extendedKeyModifier;
-const int KeyPress::endKey = VK_END | extendedKeyModifier;
-const int KeyPress::pageUpKey = VK_PRIOR | extendedKeyModifier;
-const int KeyPress::pageDownKey = VK_NEXT | extendedKeyModifier;
-const int KeyPress::F1Key = VK_F1 | extendedKeyModifier;
-const int KeyPress::F2Key = VK_F2 | extendedKeyModifier;
-const int KeyPress::F3Key = VK_F3 | extendedKeyModifier;
-const int KeyPress::F4Key = VK_F4 | extendedKeyModifier;
-const int KeyPress::F5Key = VK_F5 | extendedKeyModifier;
-const int KeyPress::F6Key = VK_F6 | extendedKeyModifier;
-const int KeyPress::F7Key = VK_F7 | extendedKeyModifier;
-const int KeyPress::F8Key = VK_F8 | extendedKeyModifier;
-const int KeyPress::F9Key = VK_F9 | extendedKeyModifier;
-const int KeyPress::F10Key = VK_F10 | extendedKeyModifier;
-const int KeyPress::F11Key = VK_F11 | extendedKeyModifier;
-const int KeyPress::F12Key = VK_F12 | extendedKeyModifier;
-const int KeyPress::F13Key = VK_F13 | extendedKeyModifier;
-const int KeyPress::F14Key = VK_F14 | extendedKeyModifier;
-const int KeyPress::F15Key = VK_F15 | extendedKeyModifier;
-const int KeyPress::F16Key = VK_F16 | extendedKeyModifier;
-const int KeyPress::numberPad0 = VK_NUMPAD0 | extendedKeyModifier;
-const int KeyPress::numberPad1 = VK_NUMPAD1 | extendedKeyModifier;
-const int KeyPress::numberPad2 = VK_NUMPAD2 | extendedKeyModifier;
-const int KeyPress::numberPad3 = VK_NUMPAD3 | extendedKeyModifier;
-const int KeyPress::numberPad4 = VK_NUMPAD4 | extendedKeyModifier;
-const int KeyPress::numberPad5 = VK_NUMPAD5 | extendedKeyModifier;
-const int KeyPress::numberPad6 = VK_NUMPAD6 | extendedKeyModifier;
-const int KeyPress::numberPad7 = VK_NUMPAD7 | extendedKeyModifier;
-const int KeyPress::numberPad8 = VK_NUMPAD8 | extendedKeyModifier;
-const int KeyPress::numberPad9 = VK_NUMPAD9 | extendedKeyModifier;
-const int KeyPress::numberPadAdd = VK_ADD | extendedKeyModifier;
-const int KeyPress::numberPadSubtract = VK_SUBTRACT | extendedKeyModifier;
-const int KeyPress::numberPadMultiply = VK_MULTIPLY | extendedKeyModifier;
-const int KeyPress::numberPadDivide = VK_DIVIDE | extendedKeyModifier;
-const int KeyPress::numberPadSeparator = VK_SEPARATOR | extendedKeyModifier;
-const int KeyPress::numberPadDecimalPoint = VK_DECIMAL | extendedKeyModifier;
-const int KeyPress::numberPadEquals = 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier;
-const int KeyPress::numberPadDelete = VK_DELETE | extendedKeyModifier;
-const int KeyPress::playKey = 0x30000;
-const int KeyPress::stopKey = 0x30001;
-const int KeyPress::fastForwardKey = 0x30002;
-const int KeyPress::rewindKey = 0x30003;
-
-
-//==============================================================================
-class WindowsBitmapImage : public Image::SharedImage
-{
-public:
- //==============================================================================
- WindowsBitmapImage (const Image::PixelFormat format_,
- const int w, const int h, const bool clearImage)
- : Image::SharedImage (format_, w, h)
- {
- jassert (format_ == Image::RGB || format_ == Image::ARGB);
-
- pixelStride = (format_ == Image::RGB) ? 3 : 4;
- lineStride = -((w * pixelStride + 3) & ~3);
-
- zerostruct (bitmapInfo);
- bitmapInfo.bV4Size = sizeof (BITMAPV4HEADER);
- bitmapInfo.bV4Width = w;
- bitmapInfo.bV4Height = h;
- bitmapInfo.bV4Planes = 1;
- bitmapInfo.bV4CSType = 1;
- bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8);
-
- if (format_ == Image::ARGB)
- {
- bitmapInfo.bV4AlphaMask = 0xff000000;
- bitmapInfo.bV4RedMask = 0xff0000;
- bitmapInfo.bV4GreenMask = 0xff00;
- bitmapInfo.bV4BlueMask = 0xff;
- bitmapInfo.bV4V4Compression = BI_BITFIELDS;
- }
- else
- {
- bitmapInfo.bV4V4Compression = BI_RGB;
- }
-
- HDC dc = GetDC (0);
- hdc = CreateCompatibleDC (dc);
- ReleaseDC (0, dc);
-
- SetMapMode (hdc, MM_TEXT);
-
- hBitmap = CreateDIBSection (hdc, (BITMAPINFO*) &(bitmapInfo), DIB_RGB_COLORS,
- (void**) &bitmapData, 0, 0);
-
- previousBitmap = SelectObject (hdc, hBitmap);
-
- if (format_ == Image::ARGB && clearImage)
- zeromem (bitmapData, std::abs (h * lineStride));
-
- imageData = bitmapData - (lineStride * (h - 1));
- }
-
- ~WindowsBitmapImage()
- {
- SelectObject (hdc, previousBitmap); // Selecting the previous bitmap before deleting the DC avoids a warning in BoundsChecker
- DeleteDC (hdc);
- DeleteObject (hBitmap);
- }
-
- Image::ImageType getType() const { return Image::NativeImage; }
-
- LowLevelGraphicsContext* createLowLevelContext()
- {
- return new LowLevelGraphicsSoftwareRenderer (Image (this));
- }
-
- void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/)
- {
- bitmap.data = imageData + x * pixelStride + y * lineStride;
- bitmap.pixelFormat = format;
- bitmap.lineStride = lineStride;
- bitmap.pixelStride = pixelStride;
- }
-
- Image::SharedImage* clone()
- {
- WindowsBitmapImage* im = new WindowsBitmapImage (format, width, height, false);
-
- for (int i = 0; i < height; ++i)
- memcpy (im->imageData + i * lineStride, imageData + i * lineStride, lineStride);
-
- return im;
- }
-
- void blitToWindow (HWND hwnd, HDC dc, const bool transparent,
- const int x, const int y,
- const RectangleList& maskedRegion,
- const uint8 updateLayeredWindowAlpha) throw()
- {
- static HDRAWDIB hdd = 0;
- static bool needToCreateDrawDib = true;
-
- if (needToCreateDrawDib)
- {
- needToCreateDrawDib = false;
-
- HDC dc = GetDC (0);
- const int n = GetDeviceCaps (dc, BITSPIXEL);
- ReleaseDC (0, dc);
-
- // only open if we're not palettised
- if (n > 8)
- hdd = DrawDibOpen();
- }
-
- SetMapMode (dc, MM_TEXT);
-
- if (transparent)
- {
- POINT p, pos;
- SIZE size;
-
- RECT windowBounds;
- GetWindowRect (hwnd, &windowBounds);
-
- p.x = -x;
- p.y = -y;
- pos.x = windowBounds.left;
- pos.y = windowBounds.top;
- size.cx = windowBounds.right - windowBounds.left;
- size.cy = windowBounds.bottom - windowBounds.top;
-
- BLENDFUNCTION bf;
- bf.AlphaFormat = AC_SRC_ALPHA;
- bf.BlendFlags = 0;
- bf.BlendOp = AC_SRC_OVER;
- bf.SourceConstantAlpha = updateLayeredWindowAlpha;
-
- if (! maskedRegion.isEmpty())
- {
- for (RectangleList::Iterator i (maskedRegion); i.next();)
- {
- const Rectangle& r = *i.getRectangle();
- ExcludeClipRect (hdc, r.getX(), r.getY(), r.getRight(), r.getBottom());
- }
- }
-
- updateLayeredWindow (hwnd, 0, &pos, &size, hdc, &p, 0, &bf, ULW_ALPHA);
- }
- else
- {
- int savedDC = 0;
-
- if (! maskedRegion.isEmpty())
- {
- savedDC = SaveDC (dc);
-
- for (RectangleList::Iterator i (maskedRegion); i.next();)
- {
- const Rectangle& r = *i.getRectangle();
- ExcludeClipRect (dc, r.getX(), r.getY(), r.getRight(), r.getBottom());
- }
- }
-
- if (hdd == 0)
- {
- StretchDIBits (dc,
- x, y, width, height,
- 0, 0, width, height,
- bitmapData, (const BITMAPINFO*) &bitmapInfo,
- DIB_RGB_COLORS, SRCCOPY);
- }
- else
- {
- DrawDibDraw (hdd, dc, x, y, -1, -1,
- (BITMAPINFOHEADER*) &bitmapInfo, bitmapData,
- 0, 0, width, height, 0);
- }
-
- if (! maskedRegion.isEmpty())
- RestoreDC (dc, savedDC);
- }
- }
-
- //==============================================================================
- HBITMAP hBitmap;
- HGDIOBJ previousBitmap;
- BITMAPV4HEADER bitmapInfo;
- HDC hdc;
- uint8* bitmapData;
- int pixelStride, lineStride;
- uint8* imageData;
-
-private:
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage);
-};
-
-namespace IconConverters
-{
- const Image createImageFromHBITMAP (HBITMAP bitmap)
- {
- Image im;
-
- if (bitmap != 0)
- {
- BITMAP bm;
-
- if (GetObject (bitmap, sizeof (BITMAP), &bm)
- && bm.bmWidth > 0 && bm.bmHeight > 0)
- {
- HDC tempDC = GetDC (0);
- HDC dc = CreateCompatibleDC (tempDC);
- ReleaseDC (0, tempDC);
-
- SelectObject (dc, bitmap);
-
- im = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true);
- Image::BitmapData imageData (im, Image::BitmapData::writeOnly);
-
- for (int y = bm.bmHeight; --y >= 0;)
- {
- for (int x = bm.bmWidth; --x >= 0;)
- {
- COLORREF col = GetPixel (dc, x, y);
-
- imageData.setPixelColour (x, y, Colour ((uint8) GetRValue (col),
- (uint8) GetGValue (col),
- (uint8) GetBValue (col)));
- }
- }
-
- DeleteDC (dc);
- }
- }
-
- return im;
- }
-
- const Image createImageFromHICON (HICON icon)
- {
- ICONINFO info;
-
- if (GetIconInfo (icon, &info))
- {
- Image mask (createImageFromHBITMAP (info.hbmMask));
- Image image (createImageFromHBITMAP (info.hbmColor));
-
- if (mask.isValid() && image.isValid())
- {
- for (int y = image.getHeight(); --y >= 0;)
- {
- for (int x = image.getWidth(); --x >= 0;)
- {
- const float brightness = mask.getPixelAt (x, y).getBrightness();
-
- if (brightness > 0.0f)
- image.multiplyAlphaAt (x, y, 1.0f - brightness);
- }
- }
-
- return image;
- }
- }
-
- return Image::null;
- }
-
- HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY)
- {
- WindowsBitmapImage* nativeBitmap = new WindowsBitmapImage (Image::ARGB, image.getWidth(), image.getHeight(), true);
- Image bitmap (nativeBitmap);
-
- {
- Graphics g (bitmap);
- g.drawImageAt (image, 0, 0);
- }
-
- HBITMAP mask = CreateBitmap (image.getWidth(), image.getHeight(), 1, 1, 0);
-
- ICONINFO info;
- info.fIcon = isIcon;
- info.xHotspot = hotspotX;
- info.yHotspot = hotspotY;
- info.hbmMask = mask;
- info.hbmColor = nativeBitmap->hBitmap;
-
- HICON hi = CreateIconIndirect (&info);
- DeleteObject (mask);
- return hi;
- }
-}
-
-//==============================================================================
-long improbableWindowNumber = 0xf965aa01; // also referenced by messaging.cpp
-
-
-//==============================================================================
-class Win32ComponentPeer : public ComponentPeer
-{
-public:
- enum RenderingEngineType
- {
- softwareRenderingEngine = 0,
- direct2DRenderingEngine
- };
-
- //==============================================================================
- Win32ComponentPeer (Component* const component,
- const int windowStyleFlags,
- HWND parentToAddTo_)
- : ComponentPeer (component, windowStyleFlags),
- dontRepaint (false),
- currentRenderingEngine (softwareRenderingEngine),
- fullScreen (false),
- isDragging (false),
- isMouseOver (false),
- hasCreatedCaret (false),
- constrainerIsResizing (false),
- currentWindowIcon (0),
- dropTarget (0),
- parentToAddTo (parentToAddTo_),
- updateLayeredWindowAlpha (255)
- {
- callFunctionIfNotLocked (&createWindowCallback, this);
-
- setTitle (component->getName());
-
- if ((windowStyleFlags & windowHasDropShadow) != 0
- && Desktop::canUseSemiTransparentWindows())
- {
- shadower = component->getLookAndFeel().createDropShadowerForComponent (component);
-
- if (shadower != 0)
- shadower->setOwner (component);
- }
- }
-
- ~Win32ComponentPeer()
- {
- setTaskBarIcon (Image());
- shadower = 0;
-
- // do this before the next bit to avoid messages arriving for this window
- // before it's destroyed
- SetWindowLongPtr (hwnd, GWLP_USERDATA, 0);
-
- callFunctionIfNotLocked (&destroyWindowCallback, (void*) hwnd);
-
- if (currentWindowIcon != 0)
- DestroyIcon (currentWindowIcon);
-
- if (dropTarget != 0)
- {
- dropTarget->Release();
- dropTarget = 0;
- }
-
- #if JUCE_DIRECT2D
- direct2DContext = 0;
- #endif
- }
-
- //==============================================================================
- void* getNativeHandle() const
- {
- return hwnd;
- }
-
- void setVisible (bool shouldBeVisible)
- {
- ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
-
- if (shouldBeVisible)
- InvalidateRect (hwnd, 0, 0);
- else
- lastPaintTime = 0;
- }
-
- void setTitle (const String& title)
- {
- SetWindowText (hwnd, title.toWideCharPointer());
- }
-
- void setPosition (int x, int y)
- {
- offsetWithinParent (x, y);
- SetWindowPos (hwnd, 0,
- x - windowBorder.getLeft(),
- y - windowBorder.getTop(),
- 0, 0,
- SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
- }
-
- void repaintNowIfTransparent()
- {
- if (isUsingUpdateLayeredWindow() && lastPaintTime > 0 && Time::getMillisecondCounter() > lastPaintTime + 30)
- handlePaintMessage();
- }
-
- void updateBorderSize()
- {
- WINDOWINFO info;
- info.cbSize = sizeof (info);
-
- if (GetWindowInfo (hwnd, &info))
- {
- windowBorder = BorderSize (info.rcClient.top - info.rcWindow.top,
- info.rcClient.left - info.rcWindow.left,
- info.rcWindow.bottom - info.rcClient.bottom,
- info.rcWindow.right - info.rcClient.right);
- }
-
- #if JUCE_DIRECT2D
- if (direct2DContext != 0)
- direct2DContext->resized();
- #endif
- }
-
- void setSize (int w, int h)
- {
- SetWindowPos (hwnd, 0, 0, 0,
- w + windowBorder.getLeftAndRight(),
- h + windowBorder.getTopAndBottom(),
- SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
-
- updateBorderSize();
-
- repaintNowIfTransparent();
- }
-
- void setBounds (int x, int y, int w, int h, bool isNowFullScreen)
- {
- fullScreen = isNowFullScreen;
- offsetWithinParent (x, y);
-
- SetWindowPos (hwnd, 0,
- x - windowBorder.getLeft(),
- y - windowBorder.getTop(),
- w + windowBorder.getLeftAndRight(),
- h + windowBorder.getTopAndBottom(),
- SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
-
- updateBorderSize();
-
- repaintNowIfTransparent();
- }
-
- const Rectangle getBounds() const
- {
- RECT r;
- GetWindowRect (hwnd, &r);
-
- Rectangle bounds (r.left, r.top, r.right - r.left, r.bottom - r.top);
-
- HWND parentH = GetParent (hwnd);
- if (parentH != 0)
- {
- GetWindowRect (parentH, &r);
- bounds.translate (-r.left, -r.top);
- }
-
- return windowBorder.subtractedFrom (bounds);
- }
-
- const Point getScreenPosition() const
- {
- RECT r;
- GetWindowRect (hwnd, &r);
- return Point (r.left + windowBorder.getLeft(),
- r.top + windowBorder.getTop());
- }
-
- const Point localToGlobal (const Point& relativePosition)
- {
- return relativePosition + getScreenPosition();
- }
-
- const Point globalToLocal (const Point& screenPosition)
- {
- return screenPosition - getScreenPosition();
- }
-
- void setAlpha (float newAlpha)
- {
- const uint8 intAlpha = (uint8) jlimit (0, 255, (int) (newAlpha * 255.0f));
-
- if (component->isOpaque())
- {
- if (newAlpha < 1.0f)
- {
- SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
- SetLayeredWindowAttributes (hwnd, RGB (0, 0, 0), intAlpha, LWA_ALPHA);
- }
- else
- {
- SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
- RedrawWindow (hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
- }
- }
- else
- {
- updateLayeredWindowAlpha = intAlpha;
- component->repaint();
- }
- }
-
- void setMinimised (bool shouldBeMinimised)
- {
- if (shouldBeMinimised != isMinimised())
- ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_SHOWNORMAL);
- }
-
- bool isMinimised() const
- {
- WINDOWPLACEMENT wp;
- wp.length = sizeof (WINDOWPLACEMENT);
- GetWindowPlacement (hwnd, &wp);
-
- return wp.showCmd == SW_SHOWMINIMIZED;
- }
-
- void setFullScreen (bool shouldBeFullScreen)
- {
- setMinimised (false);
-
- if (fullScreen != shouldBeFullScreen)
- {
- fullScreen = shouldBeFullScreen;
- const WeakReference deletionChecker (component);
-
- if (! fullScreen)
- {
- const Rectangle boundsCopy (lastNonFullscreenBounds);
-
- if (hasTitleBar())
- ShowWindow (hwnd, SW_SHOWNORMAL);
-
- if (! boundsCopy.isEmpty())
- {
- setBounds (boundsCopy.getX(),
- boundsCopy.getY(),
- boundsCopy.getWidth(),
- boundsCopy.getHeight(),
- false);
- }
- }
- else
- {
- if (hasTitleBar())
- ShowWindow (hwnd, SW_SHOWMAXIMIZED);
- else
- SendMessageW (hwnd, WM_SETTINGCHANGE, 0, 0);
- }
-
- if (deletionChecker != 0)
- handleMovedOrResized();
- }
- }
-
- bool isFullScreen() const
- {
- if (! hasTitleBar())
- return fullScreen;
-
- WINDOWPLACEMENT wp;
- wp.length = sizeof (wp);
- GetWindowPlacement (hwnd, &wp);
-
- return wp.showCmd == SW_SHOWMAXIMIZED;
- }
-
- bool contains (const Point& position, bool trueIfInAChildWindow) const
- {
- if (! (isPositiveAndBelow (position.getX(), component->getWidth())
- && isPositiveAndBelow (position.getY(), component->getHeight())))
- return false;
-
- RECT r;
- GetWindowRect (hwnd, &r);
-
- POINT p;
- p.x = position.getX() + r.left + windowBorder.getLeft();
- p.y = position.getY() + r.top + windowBorder.getTop();
-
- HWND w = WindowFromPoint (p);
- return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0));
- }
-
- const BorderSize getFrameSize() const
- {
- return windowBorder;
- }
-
- bool setAlwaysOnTop (bool alwaysOnTop)
- {
- const bool oldDeactivate = shouldDeactivateTitleBar;
- shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
-
- SetWindowPos (hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
- 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
-
- shouldDeactivateTitleBar = oldDeactivate;
-
- if (shadower != 0)
- shadower->componentBroughtToFront (*component);
-
- return true;
- }
-
- void toFront (bool makeActive)
- {
- setMinimised (false);
-
- const bool oldDeactivate = shouldDeactivateTitleBar;
- shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
-
- callFunctionIfNotLocked (makeActive ? &toFrontCallback1 : &toFrontCallback2, hwnd);
-
- shouldDeactivateTitleBar = oldDeactivate;
-
- if (! makeActive)
- {
- // in this case a broughttofront call won't have occured, so do it now..
- handleBroughtToFront();
- }
- }
-
- void toBehind (ComponentPeer* other)
- {
- Win32ComponentPeer* const otherPeer = dynamic_cast (other);
-
- jassert (otherPeer != 0); // wrong type of window?
-
- if (otherPeer != 0)
- {
- setMinimised (false);
-
- // must be careful not to try to put a topmost window behind a normal one, or win32
- // promotes the normal one to be topmost!
- if (getComponent()->isAlwaysOnTop() == otherPeer->getComponent()->isAlwaysOnTop())
- SetWindowPos (hwnd, otherPeer->hwnd, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
- else if (otherPeer->getComponent()->isAlwaysOnTop())
- SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
- }
- }
-
- bool isFocused() const
- {
- return callFunctionIfNotLocked (&getFocusCallback, 0) == (void*) hwnd;
- }
-
- void grabFocus()
- {
- const bool oldDeactivate = shouldDeactivateTitleBar;
- shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
-
- callFunctionIfNotLocked (&setFocusCallback, hwnd);
-
- shouldDeactivateTitleBar = oldDeactivate;
- }
-
- void textInputRequired (const Point&)
- {
- if (! hasCreatedCaret)
- {
- hasCreatedCaret = true;
- CreateCaret (hwnd, (HBITMAP) 1, 0, 0);
- }
-
- ShowCaret (hwnd);
- SetCaretPos (0, 0);
- }
-
- void dismissPendingTextInput()
- {
- imeHandler.handleSetContext (hwnd, false);
- }
-
- void repaint (const Rectangle& area)
- {
- const RECT r = { area.getX(), area.getY(), area.getRight(), area.getBottom() };
- InvalidateRect (hwnd, &r, FALSE);
- }
-
- void performAnyPendingRepaintsNow()
- {
- MSG m;
- if (component->isVisible()
- && (PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE) || isUsingUpdateLayeredWindow()))
- handlePaintMessage();
- }
-
- //==============================================================================
- static Win32ComponentPeer* getOwnerOfWindow (HWND h) throw()
- {
- if (h != 0 && GetWindowLongPtr (h, GWLP_USERDATA) == improbableWindowNumber)
- return (Win32ComponentPeer*) (pointer_sized_int) GetWindowLongPtr (h, 8);
-
- return 0;
- }
-
- //==============================================================================
- void setTaskBarIcon (const Image& image)
- {
- if (image.isValid())
- {
- HICON hicon = IconConverters::createHICONFromImage (image, TRUE, 0, 0);
-
- if (taskBarIcon == 0)
- {
- taskBarIcon = new NOTIFYICONDATA();
- zeromem (taskBarIcon, sizeof (NOTIFYICONDATA));
- taskBarIcon->cbSize = sizeof (NOTIFYICONDATA);
- taskBarIcon->hWnd = (HWND) hwnd;
- taskBarIcon->uID = (int) (pointer_sized_int) hwnd;
- taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
- taskBarIcon->uCallbackMessage = WM_TRAYNOTIFY;
- taskBarIcon->hIcon = hicon;
- taskBarIcon->szTip[0] = 0;
-
- Shell_NotifyIcon (NIM_ADD, taskBarIcon);
- }
- else
- {
- HICON oldIcon = taskBarIcon->hIcon;
-
- taskBarIcon->hIcon = hicon;
- taskBarIcon->uFlags = NIF_ICON;
- Shell_NotifyIcon (NIM_MODIFY, taskBarIcon);
-
- DestroyIcon (oldIcon);
- }
- }
- else if (taskBarIcon != 0)
- {
- taskBarIcon->uFlags = 0;
- Shell_NotifyIcon (NIM_DELETE, taskBarIcon);
- DestroyIcon (taskBarIcon->hIcon);
- taskBarIcon = 0;
- }
- }
-
- void setTaskBarIconToolTip (const String& toolTip) const
- {
- if (taskBarIcon != 0)
- {
- taskBarIcon->uFlags = NIF_TIP;
- toolTip.copyToUTF16 (taskBarIcon->szTip, sizeof (taskBarIcon->szTip) - 1);
- Shell_NotifyIcon (NIM_MODIFY, taskBarIcon);
- }
- }
-
- void handleTaskBarEvent (const LPARAM lParam)
- {
- if (component->isCurrentlyBlockedByAnotherModalComponent())
- {
- if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN
- || lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK)
- {
- Component* const current = Component::getCurrentlyModalComponent();
-
- if (current != 0)
- current->inputAttemptWhenModal();
- }
- }
- else
- {
- ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime());
-
- if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK)
- eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier);
- else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK)
- eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier);
- else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
- eventMods = eventMods.withoutMouseButtons();
-
- const MouseEvent e (Desktop::getInstance().getMainMouseSource(),
- Point(), eventMods, component, component, Time (getMouseEventTime()),
- Point(), Time (getMouseEventTime()), 1, false);
-
- if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
- {
- SetFocus (hwnd);
- SetForegroundWindow (hwnd);
- component->mouseDown (e);
- }
- else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
- {
- component->mouseUp (e);
- }
- else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK)
- {
- component->mouseDoubleClick (e);
- }
- else if (lParam == WM_MOUSEMOVE)
- {
- component->mouseMove (e);
- }
- }
- }
-
- //==============================================================================
- bool isInside (HWND h) const throw()
- {
- return GetAncestor (hwnd, GA_ROOT) == h;
- }
-
- //==============================================================================
- static bool isKeyDown (const int key) throw() { return (GetAsyncKeyState (key) & 0x8000) != 0; }
-
- static void updateKeyModifiers() throw()
- {
- int keyMods = 0;
- if (isKeyDown (VK_SHIFT)) keyMods |= ModifierKeys::shiftModifier;
- if (isKeyDown (VK_CONTROL)) keyMods |= ModifierKeys::ctrlModifier;
- if (isKeyDown (VK_MENU)) keyMods |= ModifierKeys::altModifier;
- if (isKeyDown (VK_RMENU)) keyMods &= ~(ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
-
- currentModifiers = currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
- }
-
- static void updateModifiersFromWParam (const WPARAM wParam)
- {
- int mouseMods = 0;
- if (wParam & MK_LBUTTON) mouseMods |= ModifierKeys::leftButtonModifier;
- if (wParam & MK_RBUTTON) mouseMods |= ModifierKeys::rightButtonModifier;
- if (wParam & MK_MBUTTON) mouseMods |= ModifierKeys::middleButtonModifier;
-
- currentModifiers = currentModifiers.withoutMouseButtons().withFlags (mouseMods);
- updateKeyModifiers();
- }
-
- static int64 getMouseEventTime()
- {
- static int64 eventTimeOffset = 0;
- static DWORD lastMessageTime = 0;
- const DWORD thisMessageTime = GetMessageTime();
-
- if (thisMessageTime < lastMessageTime || lastMessageTime == 0)
- {
- lastMessageTime = thisMessageTime;
- eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
- }
-
- return eventTimeOffset + thisMessageTime;
- }
-
- //==============================================================================
- bool dontRepaint;
-
- static ModifierKeys currentModifiers;
- static ModifierKeys modifiersAtLastCallback;
-
-private:
- HWND hwnd, parentToAddTo;
- ScopedPointer shadower;
- RenderingEngineType currentRenderingEngine;
- #if JUCE_DIRECT2D
- ScopedPointer direct2DContext;
- #endif
- bool fullScreen, isDragging, isMouseOver, hasCreatedCaret, constrainerIsResizing;
- BorderSize windowBorder;
- HICON currentWindowIcon;
- ScopedPointer taskBarIcon;
- IDropTarget* dropTarget;
- uint8 updateLayeredWindowAlpha;
-
- //==============================================================================
- class TemporaryImage : public Timer
- {
- public:
- TemporaryImage() {}
-
- const Image& getImage (const bool transparent, const int w, const int h)
- {
- const Image::PixelFormat format = transparent ? Image::ARGB : Image::RGB;
-
- if ((! image.isValid()) || image.getWidth() < w || image.getHeight() < h || image.getFormat() != format)
- image = Image (new WindowsBitmapImage (format, (w + 31) & ~31, (h + 31) & ~31, false));
-
- startTimer (3000);
- return image;
- }
-
- void timerCallback()
- {
- stopTimer();
- image = Image::null;
- }
-
- private:
- Image image;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryImage);
- };
-
- TemporaryImage offscreenImageGenerator;
-
- //==============================================================================
- class WindowClassHolder : public DeletedAtShutdown
- {
- public:
- WindowClassHolder()
- {
- // this name has to be different for each app/dll instance because otherwise poor old Win32 can
- // get a bit confused (even despite it not being a process-global window class).
- String windowClassName ("JUCE_");
- windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
-
- HINSTANCE moduleHandle = (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle();
-
- TCHAR moduleFile [1024] = { 0 };
- GetModuleFileName (moduleHandle, moduleFile, 1024);
- WORD iconNum = 0;
-
- WNDCLASSEX wcex = { 0 };
- wcex.cbSize = sizeof (wcex);
- wcex.style = CS_OWNDC;
- wcex.lpfnWndProc = (WNDPROC) windowProc;
- wcex.lpszClassName = windowClassName.toWideCharPointer();
- wcex.cbWndExtra = 32;
- wcex.hInstance = moduleHandle;
- wcex.hIcon = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
- iconNum = 1;
- wcex.hIconSm = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
-
- atom = RegisterClassEx (&wcex);
- jassert (atom != 0);
- }
-
- ~WindowClassHolder()
- {
- if (ComponentPeer::getNumPeers() == 0)
- UnregisterClass (getWindowClassName(), (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle());
-
- clearSingletonInstance();
- }
-
- LPCTSTR getWindowClassName() const throw() { return (LPCTSTR) MAKELONG (atom, 0); }
-
- juce_DeclareSingleton_SingleThreaded_Minimal (WindowClassHolder);
-
- private:
- ATOM atom;
-
- JUCE_DECLARE_NON_COPYABLE (WindowClassHolder);
- };
-
- //==============================================================================
- static void* createWindowCallback (void* userData)
- {
- static_cast (userData)->createWindow();
- return 0;
- }
-
- void createWindow()
- {
- DWORD exstyle = WS_EX_ACCEPTFILES;
- DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
-
- if (hasTitleBar())
- {
- type |= WS_OVERLAPPED;
-
- if ((styleFlags & windowHasCloseButton) != 0)
- {
- type |= WS_SYSMENU;
- }
- else
- {
- // annoyingly, windows won't let you have a min/max button without a close button
- jassert ((styleFlags & (windowHasMinimiseButton | windowHasMaximiseButton)) == 0);
- }
-
- if ((styleFlags & windowIsResizable) != 0)
- type |= WS_THICKFRAME;
- }
- else if (parentToAddTo != 0)
- {
- type |= WS_CHILD;
- }
- else
- {
- type |= WS_POPUP | WS_SYSMENU;
- }
-
- if ((styleFlags & windowAppearsOnTaskbar) == 0)
- exstyle |= WS_EX_TOOLWINDOW;
- else
- exstyle |= WS_EX_APPWINDOW;
-
- if ((styleFlags & windowHasMinimiseButton) != 0)
- type |= WS_MINIMIZEBOX;
-
- if ((styleFlags & windowHasMaximiseButton) != 0)
- type |= WS_MAXIMIZEBOX;
-
- if ((styleFlags & windowIgnoresMouseClicks) != 0)
- exstyle |= WS_EX_TRANSPARENT;
-
- if ((styleFlags & windowIsSemiTransparent) != 0 && Desktop::canUseSemiTransparentWindows())
- exstyle |= WS_EX_LAYERED;
-
- hwnd = CreateWindowEx (exstyle, WindowClassHolder::getInstance()->getWindowClassName(),
- L"", type, 0, 0, 0, 0, parentToAddTo, 0,
- (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle(), 0);
-
- #if JUCE_DIRECT2D
- setCurrentRenderingEngine (1);
- #endif
-
- if (hwnd != 0)
- {
- SetWindowLongPtr (hwnd, 0, 0);
- SetWindowLongPtr (hwnd, 8, (LONG_PTR) this);
- SetWindowLongPtr (hwnd, GWLP_USERDATA, improbableWindowNumber);
-
- if (dropTarget == 0)
- dropTarget = new JuceDropTarget (this);
-
- RegisterDragDrop (hwnd, dropTarget);
-
- updateBorderSize();
-
- // Calling this function here is (for some reason) necessary to make Windows
- // correctly enable the menu items that we specify in the wm_initmenu message.
- GetSystemMenu (hwnd, false);
-
- const float alpha = component->getAlpha();
- if (alpha < 1.0f)
- setAlpha (alpha);
- }
- else
- {
- jassertfalse;
- }
- }
-
- static void* destroyWindowCallback (void* handle)
- {
- RevokeDragDrop ((HWND) handle);
- DestroyWindow ((HWND) handle);
- return 0;
- }
-
- static void* toFrontCallback1 (void* h)
- {
- SetForegroundWindow ((HWND) h);
- return 0;
- }
-
- static void* toFrontCallback2 (void* h)
- {
- SetWindowPos ((HWND) h, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
- return 0;
- }
-
- static void* setFocusCallback (void* h)
- {
- SetFocus ((HWND) h);
- return 0;
- }
-
- static void* getFocusCallback (void*)
- {
- return GetFocus();
- }
-
- void offsetWithinParent (int& x, int& y) const
- {
- if (isUsingUpdateLayeredWindow())
- {
- HWND parentHwnd = GetParent (hwnd);
-
- if (parentHwnd != 0)
- {
- RECT parentRect;
- GetWindowRect (parentHwnd, &parentRect);
- x += parentRect.left;
- y += parentRect.top;
- }
- }
- }
-
- bool isUsingUpdateLayeredWindow() const
- {
- return ! component->isOpaque();
- }
-
- inline bool hasTitleBar() const throw() { return (styleFlags & windowHasTitleBar) != 0; }
-
-
- void setIcon (const Image& newIcon)
- {
- HICON hicon = IconConverters::createHICONFromImage (newIcon, TRUE, 0, 0);
-
- if (hicon != 0)
- {
- SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
- SendMessage (hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
-
- if (currentWindowIcon != 0)
- DestroyIcon (currentWindowIcon);
-
- currentWindowIcon = hicon;
- }
- }
-
- //==============================================================================
- void handlePaintMessage()
- {
- #if JUCE_DIRECT2D
- if (direct2DContext != 0)
- {
- RECT r;
-
- if (GetUpdateRect (hwnd, &r, false))
- {
- direct2DContext->start();
- direct2DContext->clipToRectangle (Rectangle (r.left, r.top, r.right - r.left, r.bottom - r.top));
- handlePaint (*direct2DContext);
- direct2DContext->end();
- }
- }
- else
- #endif
-
- {
- HRGN rgn = CreateRectRgn (0, 0, 0, 0);
- const int regionType = GetUpdateRgn (hwnd, rgn, false);
-
- PAINTSTRUCT paintStruct;
- HDC dc = BeginPaint (hwnd, &paintStruct); // Note this can immediately generate a WM_NCPAINT
- // message and become re-entrant, but that's OK
-
- // if something in a paint handler calls, e.g. a message box, this can become reentrant and
- // corrupt the image it's using to paint into, so do a check here.
- static bool reentrant = false;
- if (reentrant)
- {
- DeleteObject (rgn);
- EndPaint (hwnd, &paintStruct);
- return;
- }
-
- const ScopedValueSetter setter (reentrant, true, false);
-
- // this is the rectangle to update..
- int x = paintStruct.rcPaint.left;
- int y = paintStruct.rcPaint.top;
- int w = paintStruct.rcPaint.right - x;
- int h = paintStruct.rcPaint.bottom - y;
-
- const bool transparent = isUsingUpdateLayeredWindow();
-
- if (transparent)
- {
- // it's not possible to have a transparent window with a title bar at the moment!
- jassert (! hasTitleBar());
-
- RECT r;
- GetWindowRect (hwnd, &r);
- x = y = 0;
- w = r.right - r.left;
- h = r.bottom - r.top;
- }
-
- if (w > 0 && h > 0)
- {
- clearMaskedRegion();
-
- Image offscreenImage (offscreenImageGenerator.getImage (transparent, w, h));
-
- RectangleList contextClip;
- const Rectangle clipBounds (0, 0, w, h);
-
- bool needToPaintAll = true;
-
- if (regionType == COMPLEXREGION && ! transparent)
- {
- HRGN clipRgn = CreateRectRgnIndirect (&paintStruct.rcPaint);
- CombineRgn (rgn, rgn, clipRgn, RGN_AND);
- DeleteObject (clipRgn);
-
- char rgnData [8192];
- const DWORD res = GetRegionData (rgn, sizeof (rgnData), (RGNDATA*) rgnData);
-
- if (res > 0 && res <= sizeof (rgnData))
- {
- const RGNDATAHEADER* const hdr = &(((const RGNDATA*) rgnData)->rdh);
-
- if (hdr->iType == RDH_RECTANGLES
- && hdr->rcBound.right - hdr->rcBound.left >= w
- && hdr->rcBound.bottom - hdr->rcBound.top >= h)
- {
- needToPaintAll = false;
-
- const RECT* rects = (const RECT*) (rgnData + sizeof (RGNDATAHEADER));
- int num = ((RGNDATA*) rgnData)->rdh.nCount;
-
- while (--num >= 0)
- {
- if (rects->right <= x + w && rects->bottom <= y + h)
- {
- const int cx = jmax (x, (int) rects->left);
- contextClip.addWithoutMerging (Rectangle (cx - x, rects->top - y, rects->right - cx, rects->bottom - rects->top)
- .getIntersection (clipBounds));
- }
- else
- {
- needToPaintAll = true;
- break;
- }
-
- ++rects;
- }
- }
- }
- }
-
- if (needToPaintAll)
- {
- contextClip.clear();
- contextClip.addWithoutMerging (Rectangle (w, h));
- }
-
- if (transparent)
- {
- RectangleList::Iterator i (contextClip);
-
- while (i.next())
- offscreenImage.clear (*i.getRectangle());
- }
-
- // if the component's not opaque, this won't draw properly unless the platform can support this
- jassert (Desktop::canUseSemiTransparentWindows() || component->isOpaque());
-
- updateCurrentModifiers();
-
- LowLevelGraphicsSoftwareRenderer context (offscreenImage, -x, -y, contextClip);
- handlePaint (context);
-
- if (! dontRepaint)
- static_cast (offscreenImage.getSharedImage())
- ->blitToWindow (hwnd, dc, transparent, x, y, maskedRegion, updateLayeredWindowAlpha);
- }
-
- DeleteObject (rgn);
- EndPaint (hwnd, &paintStruct);
- }
-
- #ifndef JUCE_GCC
- _fpreset(); // because some graphics cards can unmask FP exceptions
- #endif
-
- lastPaintTime = Time::getMillisecondCounter();
- }
-
- //==============================================================================
- void doMouseEvent (const Point& position)
- {
- handleMouseEvent (0, position, currentModifiers, getMouseEventTime());
- }
-
- const StringArray getAvailableRenderingEngines()
- {
- StringArray s (ComponentPeer::getAvailableRenderingEngines());
-
- #if JUCE_DIRECT2D
- if (SystemStats::getOperatingSystemType() >= SystemStats::Windows7)
- s.add ("Direct2D");
- #endif
-
- return s;
- }
-
- int getCurrentRenderingEngine() const { return currentRenderingEngine; }
-
- #if JUCE_DIRECT2D
- void updateDirect2DContext()
- {
- if (currentRenderingEngine != direct2DRenderingEngine)
- direct2DContext = 0;
- else if (direct2DContext == 0)
- direct2DContext = new Direct2DLowLevelGraphicsContext (hwnd);
- }
- #endif
-
- void setCurrentRenderingEngine (int index)
- {
- (void) index;
-
- #if JUCE_DIRECT2D
- if (getAvailableRenderingEngines().size() > 1)
- {
- currentRenderingEngine = index == 1 ? direct2DRenderingEngine : softwareRenderingEngine;
- updateDirect2DContext();
- repaint (component->getLocalBounds());
- }
- #endif
- }
-
- void doMouseMove (const Point& position)
- {
- if (! isMouseOver)
- {
- isMouseOver = true;
- updateKeyModifiers();
-
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof (tme);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hwnd;
- tme.dwHoverTime = 0;
-
- if (! TrackMouseEvent (&tme))
- jassertfalse;
-
- Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
- }
- else if (! isDragging)
- {
- if (! contains (position, false))
- return;
- }
-
- // (Throttling the incoming queue of mouse-events seems to still be required in XP..)
- static uint32 lastMouseTime = 0;
- const uint32 now = Time::getMillisecondCounter();
- const int maxMouseMovesPerSecond = 60;
-
- if (now > lastMouseTime + 1000 / maxMouseMovesPerSecond)
- {
- lastMouseTime = now;
- doMouseEvent (position);
- }
- }
-
- void doMouseDown (const Point& position, const WPARAM wParam)
- {
- if (GetCapture() != hwnd)
- SetCapture (hwnd);
-
- doMouseMove (position);
-
- updateModifiersFromWParam (wParam);
- isDragging = true;
-
- doMouseEvent (position);
- }
-
- void doMouseUp (const Point& position, const WPARAM wParam)
- {
- updateModifiersFromWParam (wParam);
- isDragging = false;
-
- // release the mouse capture if the user has released all buttons
- if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) == 0 && hwnd == GetCapture())
- ReleaseCapture();
-
- doMouseEvent (position);
- }
-
- void doCaptureChanged()
- {
- if (constrainerIsResizing)
- {
- if (constrainer != 0)
- constrainer->resizeEnd();
-
- constrainerIsResizing = false;
- }
-
- if (isDragging)
- doMouseUp (getCurrentMousePos(), (WPARAM) 0);
- }
-
- void doMouseExit()
- {
- isMouseOver = false;
- doMouseEvent (getCurrentMousePos());
- }
-
- void doMouseWheel (const Point& position, const WPARAM wParam, const bool isVertical)
- {
- updateKeyModifiers();
-
- const float amount = jlimit (-1000.0f, 1000.0f, 0.75f * (short) HIWORD (wParam));
-
- handleMouseWheel (0, position, getMouseEventTime(),
- isVertical ? 0.0f : amount,
- isVertical ? amount : 0.0f);
- }
-
- //==============================================================================
- void sendModifierKeyChangeIfNeeded()
- {
- if (modifiersAtLastCallback != currentModifiers)
- {
- modifiersAtLastCallback = currentModifiers;
- handleModifierKeysChange();
- }
- }
-
- bool doKeyUp (const WPARAM key)
- {
- updateKeyModifiers();
-
- switch (key)
- {
- case VK_SHIFT:
- case VK_CONTROL:
- case VK_MENU:
- case VK_CAPITAL:
- case VK_LWIN:
- case VK_RWIN:
- case VK_APPS:
- case VK_NUMLOCK:
- case VK_SCROLL:
- case VK_LSHIFT:
- case VK_RSHIFT:
- case VK_LCONTROL:
- case VK_LMENU:
- case VK_RCONTROL:
- case VK_RMENU:
- sendModifierKeyChangeIfNeeded();
- }
-
- return handleKeyUpOrDown (false)
- || Component::getCurrentlyModalComponent() != 0;
- }
-
- bool doKeyDown (const WPARAM key)
- {
- updateKeyModifiers();
- bool used = false;
-
- switch (key)
- {
- case VK_SHIFT:
- case VK_LSHIFT:
- case VK_RSHIFT:
- case VK_CONTROL:
- case VK_LCONTROL:
- case VK_RCONTROL:
- case VK_MENU:
- case VK_LMENU:
- case VK_RMENU:
- case VK_LWIN:
- case VK_RWIN:
- case VK_CAPITAL:
- case VK_NUMLOCK:
- case VK_SCROLL:
- case VK_APPS:
- sendModifierKeyChangeIfNeeded();
- break;
-
- case VK_LEFT:
- case VK_RIGHT:
- case VK_UP:
- case VK_DOWN:
- case VK_PRIOR:
- case VK_NEXT:
- case VK_HOME:
- case VK_END:
- case VK_DELETE:
- case VK_INSERT:
- case VK_F1:
- case VK_F2:
- case VK_F3:
- case VK_F4:
- case VK_F5:
- case VK_F6:
- case VK_F7:
- case VK_F8:
- case VK_F9:
- case VK_F10:
- case VK_F11:
- case VK_F12:
- case VK_F13:
- case VK_F14:
- case VK_F15:
- case VK_F16:
- used = handleKeyUpOrDown (true);
- used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used;
- break;
-
- case VK_ADD:
- case VK_SUBTRACT:
- case VK_MULTIPLY:
- case VK_DIVIDE:
- case VK_SEPARATOR:
- case VK_DECIMAL:
- used = handleKeyUpOrDown (true);
- break;
-
- default:
- used = handleKeyUpOrDown (true);
-
- {
- MSG msg;
-
- if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE))
- {
- // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to
- // manually generate the key-press event that matches this key-down.
-
- const UINT keyChar = MapVirtualKey (key, 2);
- used = handleKeyPress ((int) LOWORD (keyChar), 0) || used;
- }
- }
-
- break;
- }
-
- if (Component::getCurrentlyModalComponent() != 0)
- used = true;
-
- return used;
- }
-
- bool doKeyChar (int key, const LPARAM flags)
- {
- updateKeyModifiers();
-
- juce_wchar textChar = (juce_wchar) key;
-
- const int virtualScanCode = (flags >> 16) & 0xff;
-
- if (key >= '0' && key <= '9')
- {
- switch (virtualScanCode) // check for a numeric keypad scan-code
- {
- case 0x52:
- case 0x4f:
- case 0x50:
- case 0x51:
- case 0x4b:
- case 0x4c:
- case 0x4d:
- case 0x47:
- case 0x48:
- case 0x49:
- key = (key - '0') + KeyPress::numberPad0;
- break;
- default:
- break;
- }
- }
- else
- {
- // convert the scan code to an unmodified character code..
- const UINT virtualKey = MapVirtualKey (virtualScanCode, 1);
- UINT keyChar = MapVirtualKey (virtualKey, 2);
-
- keyChar = LOWORD (keyChar);
-
- if (keyChar != 0)
- key = (int) keyChar;
-
- // avoid sending junk text characters for some control-key combinations
- if (textChar < ' ' && currentModifiers.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::altModifier))
- textChar = 0;
- }
-
- return handleKeyPress (key, textChar);
- }
-
- void forwardMessageToParent (UINT message, WPARAM wParam, LPARAM lParam) const
- {
- HWND parentH = GetParent (hwnd);
- if (parentH != 0)
- PostMessage (parentH, message, wParam, lParam);
- }
-
- bool doAppCommand (const LPARAM lParam)
- {
- int key = 0;
-
- switch (GET_APPCOMMAND_LPARAM (lParam))
- {
- case APPCOMMAND_MEDIA_PLAY_PAUSE: key = KeyPress::playKey; break;
- case APPCOMMAND_MEDIA_STOP: key = KeyPress::stopKey; break;
- case APPCOMMAND_MEDIA_NEXTTRACK: key = KeyPress::fastForwardKey; break;
- case APPCOMMAND_MEDIA_PREVIOUSTRACK: key = KeyPress::rewindKey; break;
- default: break;
- }
-
- if (key != 0)
- {
- updateKeyModifiers();
-
- if (hwnd == GetActiveWindow())
- {
- handleKeyPress (key, 0);
- return true;
- }
- }
-
- return false;
- }
-
- bool isConstrainedNativeWindow() const
- {
- return constrainer != 0
- && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable);
- }
-
- LRESULT handleSizeConstraining (RECT* const r, const WPARAM wParam)
- {
- if (isConstrainedNativeWindow())
- {
- Rectangle pos (r->left, r->top, r->right - r->left, r->bottom - r->top);
-
- constrainer->checkBounds (pos, windowBorder.addedTo (component->getBounds()),
- Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),
- wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT,
- wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT,
- wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT,
- wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT);
- r->left = pos.getX();
- r->top = pos.getY();
- r->right = pos.getRight();
- r->bottom = pos.getBottom();
- }
-
- return TRUE;
- }
-
- LRESULT handlePositionChanging (WINDOWPOS* const wp)
- {
- if (isConstrainedNativeWindow())
- {
- if ((wp->flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)
- && ! Component::isMouseButtonDownAnywhere())
- {
- Rectangle pos (wp->x, wp->y, wp->cx, wp->cy);
- const Rectangle current (windowBorder.addedTo (component->getBounds()));
-
- constrainer->checkBounds (pos, current,
- Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),
- pos.getY() != current.getY() && pos.getBottom() == current.getBottom(),
- pos.getX() != current.getX() && pos.getRight() == current.getRight(),
- pos.getY() == current.getY() && pos.getBottom() != current.getBottom(),
- pos.getX() == current.getX() && pos.getRight() != current.getRight());
- wp->x = pos.getX();
- wp->y = pos.getY();
- wp->cx = pos.getWidth();
- wp->cy = pos.getHeight();
- }
- }
-
- return 0;
- }
-
- void handleAppActivation (const WPARAM wParam)
- {
- modifiersAtLastCallback = -1;
- updateKeyModifiers();
-
- if (isMinimised())
- {
- component->repaint();
- handleMovedOrResized();
-
- if (! ComponentPeer::isValidPeer (this))
- return;
- }
-
- Component* underMouse = component->getComponentAt (component->getMouseXYRelative());
-
- if (underMouse == 0)
- underMouse = component;
-
- if (underMouse->isCurrentlyBlockedByAnotherModalComponent())
- {
- if (LOWORD (wParam) == WA_CLICKACTIVE)
- Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
- else
- ModalComponentManager::getInstance()->bringModalComponentsToFront();
- }
- else
- {
- handleBroughtToFront();
- }
- }
-
- void handleLeftClickInNCArea (WPARAM wParam)
- {
- if (! sendInputAttemptWhenModalMessage())
- {
- switch (wParam)
- {
- case HTBOTTOM:
- case HTBOTTOMLEFT:
- case HTBOTTOMRIGHT:
- case HTGROWBOX:
- case HTLEFT:
- case HTRIGHT:
- case HTTOP:
- case HTTOPLEFT:
- case HTTOPRIGHT:
- if (isConstrainedNativeWindow())
- {
- constrainerIsResizing = true;
- constrainer->resizeStart();
- }
- break;
-
- default:
- break;
- }
- }
- }
-
- //==============================================================================
- class JuceDropTarget : public ComBaseClassHelper
- {
- public:
- JuceDropTarget (Win32ComponentPeer* const owner_)
- : owner (owner_)
- {
- }
-
- HRESULT __stdcall DragEnter (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
- {
- updateFileList (pDataObject);
- owner->handleFileDragMove (files, owner->globalToLocal (Point (mousePos.x, mousePos.y)));
- *pdwEffect = DROPEFFECT_COPY;
- return S_OK;
- }
-
- HRESULT __stdcall DragLeave()
- {
- owner->handleFileDragExit (files);
- return S_OK;
- }
-
- HRESULT __stdcall DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
- {
- owner->handleFileDragMove (files, owner->globalToLocal (Point (mousePos.x, mousePos.y)));
- *pdwEffect = DROPEFFECT_COPY;
- return S_OK;
- }
-
- HRESULT __stdcall Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
- {
- updateFileList (pDataObject);
- owner->handleFileDragDrop (files, owner->globalToLocal (Point (mousePos.x, mousePos.y)));
- *pdwEffect = DROPEFFECT_COPY;
- return S_OK;
- }
-
- private:
- Win32ComponentPeer* const owner;
- StringArray files;
-
- void updateFileList (IDataObject* const pDataObject)
- {
- files.clear();
-
- FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
- STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
-
- if (pDataObject->GetData (&format, &medium) == S_OK)
- {
- const SIZE_T totalLen = GlobalSize (medium.hGlobal);
- const LPDROPFILES pDropFiles = (const LPDROPFILES) GlobalLock (medium.hGlobal);
- unsigned int i = 0;
-
- if (pDropFiles->fWide)
- {
- const WCHAR* const fname = (WCHAR*) addBytesToPointer (pDropFiles, sizeof (DROPFILES));
-
- for (;;)
- {
- unsigned int len = 0;
- while (i + len < totalLen && fname [i + len] != 0)
- ++len;
-
- if (len == 0)
- break;
-
- files.add (String (fname + i, len));
- i += len + 1;
- }
- }
- else
- {
- const char* const fname = (const char*) addBytesToPointer (pDropFiles, sizeof (DROPFILES));
-
- for (;;)
- {
- unsigned int len = 0;
- while (i + len < totalLen && fname [i + len] != 0)
- ++len;
-
- if (len == 0)
- break;
-
- files.add (String (fname + i, len));
- i += len + 1;
- }
- }
-
- GlobalUnlock (medium.hGlobal);
- }
- }
-
- JUCE_DECLARE_NON_COPYABLE (JuceDropTarget);
- };
-
- void doSettingChange()
- {
- Desktop::getInstance().refreshMonitorSizes();
-
- if (fullScreen && ! isMinimised())
- {
- const Rectangle r (component->getParentMonitorArea());
-
- SetWindowPos (hwnd, 0, r.getX(), r.getY(), r.getWidth(), r.getHeight(),
- SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
- }
- }
-
- //==============================================================================
-public:
- static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
- {
- Win32ComponentPeer* const peer = getOwnerOfWindow (h);
-
- if (peer != 0)
- {
- jassert (isValidPeer (peer));
- return peer->peerWindowProc (h, message, wParam, lParam);
- }
-
- return DefWindowProcW (h, message, wParam, lParam);
- }
-
-private:
- static void* callFunctionIfNotLocked (MessageCallbackFunction* callback, void* userData)
- {
- if (MessageManager::getInstance()->currentThreadHasLockedMessageManager())
- return callback (userData);
- else
- return MessageManager::getInstance()->callFunctionOnMessageThread (callback, userData);
- }
-
- static const Point getPointFromLParam (LPARAM lParam) throw()
- {
- return Point (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
- }
-
- const Point getCurrentMousePos() throw()
- {
- RECT wr;
- GetWindowRect (hwnd, &wr);
- const DWORD mp = GetMessagePos();
-
- return Point (GET_X_LPARAM (mp) - wr.left - windowBorder.getLeft(),
- GET_Y_LPARAM (mp) - wr.top - windowBorder.getTop());
- }
-
- LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch (message)
- {
- //==============================================================================
- case WM_NCHITTEST:
- if ((styleFlags & windowIgnoresMouseClicks) != 0)
- return HTTRANSPARENT;
- else if (! hasTitleBar())
- return HTCLIENT;
-
- break;
-
- //==============================================================================
- case WM_PAINT:
- handlePaintMessage();
- return 0;
-
- case WM_NCPAINT:
- if (wParam != 1) // (1 = a repaint of the entire NC region)
- handlePaintMessage(); // this must be done, even with native titlebars, or there are rendering artifacts.
-
- if (hasTitleBar())
- break; // let the DefWindowProc handle drawing the frame.
-
- return 0;
-
- case WM_ERASEBKGND:
- case WM_NCCALCSIZE:
- if (hasTitleBar())
- break;
-
- return 1;
-
- //==============================================================================
- case WM_MOUSEMOVE:
- doMouseMove (getPointFromLParam (lParam));
- return 0;
-
- case WM_MOUSELEAVE:
- doMouseExit();
- return 0;
-
- case WM_LBUTTONDOWN:
- case WM_MBUTTONDOWN:
- case WM_RBUTTONDOWN:
- doMouseDown (getPointFromLParam (lParam), wParam);
- return 0;
-
- case WM_LBUTTONUP:
- case WM_MBUTTONUP:
- case WM_RBUTTONUP:
- doMouseUp (getPointFromLParam (lParam), wParam);
- return 0;
-
- case WM_CAPTURECHANGED:
- doCaptureChanged();
- return 0;
-
- case WM_NCMOUSEMOVE:
- if (hasTitleBar())
- break;
-
- return 0;
-
- case 0x020A: /* WM_MOUSEWHEEL */
- case 0x020E: /* WM_MOUSEHWHEEL */
- doMouseWheel (getCurrentMousePos(), wParam, message == 0x020A);
- return 0;
-
- //==============================================================================
- case WM_SIZING:
- return handleSizeConstraining ((RECT*) lParam, wParam);
-
- case WM_WINDOWPOSCHANGING:
- return handlePositionChanging ((WINDOWPOS*) lParam);
-
- case WM_WINDOWPOSCHANGED:
- {
- const Point pos (getCurrentMousePos());
- if (contains (pos, false))
- doMouseEvent (pos);
- }
-
- handleMovedOrResized();
-
- if (dontRepaint)
- break; // needed for non-accelerated openGL windows to draw themselves correctly..
-
- return 0;
-
- //==============================================================================
- case WM_KEYDOWN:
- case WM_SYSKEYDOWN:
- if (doKeyDown (wParam))
- return 0;
- else
- forwardMessageToParent (message, wParam, lParam);
-
- break;
-
- case WM_KEYUP:
- case WM_SYSKEYUP:
- if (doKeyUp (wParam))
- return 0;
- else
- forwardMessageToParent (message, wParam, lParam);
-
- break;
-
- case WM_CHAR:
- if (doKeyChar ((int) wParam, lParam))
- return 0;
- else
- forwardMessageToParent (message, wParam, lParam);
-
- break;
-
- case WM_APPCOMMAND:
- if (doAppCommand (lParam))
- return TRUE;
-
- break;
-
- //==============================================================================
- case WM_SETFOCUS:
- updateKeyModifiers();
- handleFocusGain();
- break;
-
- case WM_KILLFOCUS:
- if (hasCreatedCaret)
- {
- hasCreatedCaret = false;
- DestroyCaret();
- }
-
- handleFocusLoss();
- break;
-
- case WM_ACTIVATEAPP:
- // Windows does weird things to process priority when you swap apps,
- // so this forces an update when the app is brought to the front
- if (wParam != FALSE)
- juce_repeatLastProcessPriority();
- else
- Desktop::getInstance().setKioskModeComponent (0); // turn kiosk mode off if we lose focus
-
- juce_CheckCurrentlyFocusedTopLevelWindow();
- modifiersAtLastCallback = -1;
- return 0;
-
- case WM_ACTIVATE:
- if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE)
- {
- handleAppActivation (wParam);
- return 0;
- }
-
- break;
-
- case WM_NCACTIVATE:
- // while a temporary window is being shown, prevent Windows from deactivating the
- // title bars of our main windows.
- if (wParam == 0 && ! shouldDeactivateTitleBar)
- wParam = TRUE; // change this and let it get passed to the DefWindowProc.
-
- break;
-
- case WM_MOUSEACTIVATE:
- if (! component->getMouseClickGrabsKeyboardFocus())
- return MA_NOACTIVATE;
-
- break;
-
- case WM_SHOWWINDOW:
- if (wParam != 0)
- handleBroughtToFront();
-
- break;
-
- case WM_CLOSE:
- if (! component->isCurrentlyBlockedByAnotherModalComponent())
- handleUserClosingWindow();
-
- return 0;
-
- case WM_QUERYENDSESSION:
- if (JUCEApplication::getInstance() != 0)
- {
- JUCEApplication::getInstance()->systemRequestedQuit();
- return MessageManager::getInstance()->hasStopMessageBeenSent();
- }
- return TRUE;
-
- case WM_TRAYNOTIFY:
- handleTaskBarEvent (lParam);
- break;
-
- case WM_SYNCPAINT:
- return 0;
-
- case WM_DISPLAYCHANGE:
- InvalidateRect (h, 0, 0);
- // intentional fall-through...
- case WM_SETTINGCHANGE: // note the fall-through in the previous case!
- doSettingChange();
- break;
-
- case WM_INITMENU:
- if (! hasTitleBar())
- {
- if (isFullScreen())
- {
- EnableMenuItem ((HMENU) wParam, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
- EnableMenuItem ((HMENU) wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
- }
- else if (! isMinimised())
- {
- EnableMenuItem ((HMENU) wParam, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
- }
- }
- break;
-
- case WM_SYSCOMMAND:
- switch (wParam & 0xfff0)
- {
- case SC_CLOSE:
- if (sendInputAttemptWhenModalMessage())
- return 0;
-
- if (hasTitleBar())
- {
- PostMessage (h, WM_CLOSE, 0, 0);
- return 0;
- }
- break;
-
- case SC_KEYMENU:
- // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure
- // situations that can arise if a modal loop is started from an alt-key keypress).
- if (hasTitleBar() && h == GetCapture())
- ReleaseCapture();
-
- break;
-
- case SC_MAXIMIZE:
- if (! sendInputAttemptWhenModalMessage())
- setFullScreen (true);
-
- return 0;
-
- case SC_MINIMIZE:
- if (sendInputAttemptWhenModalMessage())
- return 0;
-
- if (! hasTitleBar())
- {
- setMinimised (true);
- return 0;
- }
- break;
-
- case SC_RESTORE:
- if (sendInputAttemptWhenModalMessage())
- return 0;
-
- if (hasTitleBar())
- {
- if (isFullScreen())
- {
- setFullScreen (false);
- return 0;
- }
- }
- else
- {
- if (isMinimised())
- setMinimised (false);
- else if (isFullScreen())
- setFullScreen (false);
-
- return 0;
- }
- break;
- }
-
- break;
-
- case WM_NCLBUTTONDOWN:
- handleLeftClickInNCArea (wParam);
- break;
-
- case WM_NCRBUTTONDOWN:
- case WM_NCMBUTTONDOWN:
- sendInputAttemptWhenModalMessage();
- break;
-
- case WM_IME_SETCONTEXT:
- imeHandler.handleSetContext (h, wParam == TRUE);
- lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
- break;
-
- case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0;
- case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); break;
- case WM_IME_COMPOSITION: imeHandler.handleComposition (*this, h, lParam); return 0;
-
- case WM_GETDLGCODE:
- return DLGC_WANTALLKEYS;
-
- default:
- if (taskBarIcon != 0)
- {
- static const DWORD taskbarCreatedMessage = RegisterWindowMessage (TEXT("TaskbarCreated"));
-
- if (message == taskbarCreatedMessage)
- {
- taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
- Shell_NotifyIcon (NIM_ADD, taskBarIcon);
- }
- }
-
- break;
- }
-
- return DefWindowProcW (h, message, wParam, lParam);
- }
-
- bool sendInputAttemptWhenModalMessage()
- {
- if (component->isCurrentlyBlockedByAnotherModalComponent())
- {
- Component* const current = Component::getCurrentlyModalComponent();
-
- if (current != 0)
- current->inputAttemptWhenModal();
-
- return true;
- }
-
- return false;
- }
-
- //==============================================================================
- class IMEHandler
- {
- public:
- IMEHandler()
- {
- reset();
- }
-
- void handleSetContext (HWND hWnd, const bool windowIsActive)
- {
- if (compositionInProgress && ! windowIsActive)
- {
- compositionInProgress = false;
-
- HIMC hImc = ImmGetContext (hWnd);
- if (hImc != 0)
- {
- ImmNotifyIME (hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
- ImmReleaseContext (hWnd, hImc);
- }
- }
- }
-
- void handleStartComposition (ComponentPeer& owner)
- {
- reset();
- TextInputTarget* const target = owner.findCurrentTextInputTarget();
-
- if (target != 0)
- target->insertTextAtCaret (String::empty);
- }
-
- void handleEndComposition (ComponentPeer& owner, HWND hWnd)
- {
- if (compositionInProgress)
- {
- // If this occurs, the user has cancelled the composition, so clear their changes..
- TextInputTarget* const target = owner.findCurrentTextInputTarget();
-
- if (target != 0)
- {
- target->setHighlightedRegion (compositionRange);
- target->insertTextAtCaret (String::empty);
- compositionRange.setLength (0);
-
- target->setHighlightedRegion (Range::emptyRange (compositionRange.getEnd()));
- target->setTemporaryUnderlining (Array >());
- }
-
- HIMC hImc = ImmGetContext (hWnd);
-
- if (hImc != 0)
- {
- ImmNotifyIME (hImc, NI_CLOSECANDIDATE, 0, 0);
- ImmReleaseContext (hWnd, hImc);
- }
- }
-
- reset();
- }
-
- void handleComposition (ComponentPeer& owner, HWND hWnd, const LPARAM lParam)
- {
- TextInputTarget* const target = owner.findCurrentTextInputTarget();
- HIMC hImc = ImmGetContext (hWnd);
-
- if (target == 0 || hImc == 0)
- return;
-
- if (compositionRange.getStart() < 0)
- compositionRange = Range::emptyRange (target->getHighlightedRegion().getStart());
-
- if ((lParam & GCS_RESULTSTR) != 0) // (composition has finished)
- {
- replaceCurrentSelection (target, getCompositionString (hImc, GCS_RESULTSTR),
- Range::emptyRange (compositionRange.getEnd()));
-
- target->setTemporaryUnderlining (Array >());
-
- compositionInProgress = false;
- }
- else if ((lParam & GCS_COMPSTR) != 0) // (composition is still in-progress)
- {
- const String newContent (getCompositionString (hImc, GCS_COMPSTR));
- const Range selection (getCompositionSelection (hImc, lParam));
-
- replaceCurrentSelection (target, newContent, selection);
-
- target->setTemporaryUnderlining (getCompositionUnderlines (hImc, lParam));
- compositionInProgress = true;
- }
-
- moveCandidateWindowToLeftAlignWithSelection (hImc, owner, target);
- ImmReleaseContext (hWnd, hImc);
- }
-
- private:
- //==============================================================================
- Range compositionRange; // The range being modified in the TextInputTarget
- bool compositionInProgress;
-
- //==============================================================================
- void reset()
- {
- compositionRange = Range::emptyRange (-1);
- compositionInProgress = false;
- }
-
- const String getCompositionString (HIMC hImc, const DWORD type) const
- {
- jassert (hImc != 0);
-
- const int stringSizeBytes = ImmGetCompositionString (hImc, type, 0, 0);
-
- if (stringSizeBytes > 0)
- {
- HeapBlock buffer;
- buffer.calloc (stringSizeBytes / sizeof (TCHAR) + 1);
- ImmGetCompositionString (hImc, type, buffer, stringSizeBytes);
- return String (buffer);
- }
-
- return String::empty;
- }
-
- int getCompositionCaretPos (HIMC hImc, LPARAM lParam, const String& currentIMEString) const
- {
- jassert (hImc != 0);
-
- if ((lParam & CS_NOMOVECARET) != 0)
- return compositionRange.getStart();
-
- if ((lParam & GCS_CURSORPOS) != 0)
- {
- const int localCaretPos = ImmGetCompositionString (hImc, GCS_CURSORPOS, 0, 0);
- return compositionRange.getStart() + jmax (0, localCaretPos);
- }
-
- return compositionRange.getStart() + currentIMEString.length();
- }
-
- // Get selected/highlighted range while doing composition:
- // returned range is relative to beginning of TextInputTarget, not composition string
- const Range getCompositionSelection (HIMC hImc, LPARAM lParam) const
- {
- jassert (hImc != 0);
- int selectionStart = 0;
- int selectionEnd = 0;
-
- if ((lParam & GCS_COMPATTR) != 0)
- {
- // Get size of attributes array:
- const int attributeSizeBytes = ImmGetCompositionString (hImc, GCS_COMPATTR, 0, 0);
-
- if (attributeSizeBytes > 0)
- {
- // Get attributes (8 bit flag per character):
- HeapBlock attributes (attributeSizeBytes);
- ImmGetCompositionString (hImc, GCS_COMPATTR, attributes, attributeSizeBytes);
-
- selectionStart = 0;
-
- for (selectionStart = 0; selectionStart < attributeSizeBytes; ++selectionStart)
- if (attributes[selectionStart] == ATTR_TARGET_CONVERTED || attributes[selectionStart] == ATTR_TARGET_NOTCONVERTED)
- break;
-
- for (selectionEnd = selectionStart; selectionEnd < attributeSizeBytes; ++selectionEnd)
- if (attributes [selectionEnd] != ATTR_TARGET_CONVERTED && attributes[selectionEnd] != ATTR_TARGET_NOTCONVERTED)
- break;
- }
- }
-
- return Range (selectionStart, selectionEnd) + compositionRange.getStart();
- }
-
- void replaceCurrentSelection (TextInputTarget* const target, const String& newContent, const Range& newSelection)
- {
- target->setHighlightedRegion (compositionRange);
- target->insertTextAtCaret (newContent);
- compositionRange.setLength (newContent.length());
-
- target->setHighlightedRegion (newSelection);
- }
-
- const Array > getCompositionUnderlines (HIMC hImc, LPARAM lParam) const
- {
- Array > result;
-
- if (hImc != 0 && (lParam & GCS_COMPCLAUSE) != 0)
- {
- const int clauseDataSizeBytes = ImmGetCompositionString (hImc, GCS_COMPCLAUSE, 0, 0);
-
- if (clauseDataSizeBytes > 0)
- {
- const int numItems = clauseDataSizeBytes / sizeof (uint32);
- HeapBlock clauseData (numItems);
-
- if (ImmGetCompositionString (hImc, GCS_COMPCLAUSE, clauseData, clauseDataSizeBytes) > 0)
- for (int i = 0; i < numItems - 1; ++i)
- result.add (Range (clauseData [i], clauseData [i + 1]) + compositionRange.getStart());
- }
- }
-
- return result;
- }
-
- void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc, ComponentPeer& peer, TextInputTarget* target) const
- {
- Component* const targetComp = dynamic_cast (target);
-
- if (targetComp != 0)
- {
- const Rectangle area (peer.getComponent()
- ->getLocalArea (targetComp, target->getCaretRectangle()));
-
- CANDIDATEFORM pos = { 0, CFS_CANDIDATEPOS, { area.getX(), area.getBottom() }, { 0, 0, 0, 0 } };
- ImmSetCandidateWindow (hImc, &pos);
- }
- }
-
- JUCE_DECLARE_NON_COPYABLE (IMEHandler);
- };
-
- IMEHandler imeHandler;
-
- //==============================================================================
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32ComponentPeer);
-};
-
-ModifierKeys Win32ComponentPeer::currentModifiers;
-ModifierKeys Win32ComponentPeer::modifiersAtLastCallback;
-
-ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
-{
- return new Win32ComponentPeer (this, styleFlags, (HWND) nativeWindowToAttachTo);
-}
-
-juce_ImplementSingleton_SingleThreaded (Win32ComponentPeer::WindowClassHolder);
-
-
-//==============================================================================
-void ModifierKeys::updateCurrentModifiers() throw()
-{
- currentModifiers = Win32ComponentPeer::currentModifiers;
-}
-
-const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw()
-{
- Win32ComponentPeer::updateKeyModifiers();
-
- int mouseMods = 0;
- if (Win32ComponentPeer::isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier;
- if (Win32ComponentPeer::isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier;
- if (Win32ComponentPeer::isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier;
-
- Win32ComponentPeer::currentModifiers
- = Win32ComponentPeer::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
-
- return Win32ComponentPeer::currentModifiers;
-}
-
-//==============================================================================
-bool KeyPress::isKeyCurrentlyDown (const int keyCode)
-{
- SHORT k = (SHORT) keyCode;
-
- if ((keyCode & extendedKeyModifier) == 0
- && (k >= (SHORT) 'a' && k <= (SHORT) 'z'))
- k += (SHORT) 'A' - (SHORT) 'a';
-
- const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA,
- (SHORT) '+', VK_OEM_PLUS,
- (SHORT) '-', VK_OEM_MINUS,
- (SHORT) '.', VK_OEM_PERIOD,
- (SHORT) ';', VK_OEM_1,
- (SHORT) ':', VK_OEM_1,
- (SHORT) '/', VK_OEM_2,
- (SHORT) '?', VK_OEM_2,
- (SHORT) '[', VK_OEM_4,
- (SHORT) ']', VK_OEM_6 };
-
- for (int i = 0; i < numElementsInArray (translatedValues); i += 2)
- if (k == translatedValues [i])
- k = translatedValues [i + 1];
-
- return Win32ComponentPeer::isKeyDown (k);
-}
-
-//==============================================================================
-void SystemTrayIconComponent::setIconImage (const Image& newImage)
-{
- Win32ComponentPeer* const wp = dynamic_cast (getPeer());
-
- if (wp != 0)
- wp->setTaskBarIcon (newImage);
-}
-
-void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
-{
- Win32ComponentPeer* const wp = dynamic_cast (getPeer());
-
- if (wp != 0)
- wp->setTaskBarIconToolTip (tooltip);
-}
-
-//==============================================================================
-void juce_setWindowStyleBit (HWND h, const int styleType, const int feature, const bool bitIsSet) throw()
-{
- DWORD val = GetWindowLong (h, styleType);
-
- if (bitIsSet)
- val |= feature;
- else
- val &= ~feature;
-
- SetWindowLongPtr (h, styleType, val);
- SetWindowPos (h, 0, 0, 0, 0, 0,
- SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
- | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSENDCHANGING);
-}
-
-
-//==============================================================================
-bool Process::isForegroundProcess()
-{
- HWND fg = GetForegroundWindow();
-
- if (fg == 0)
- return true;
-
- // when running as a plugin in IE8, the browser UI runs in a different process to the plugin, so
- // process ID isn't a reliable way to check if the foreground window belongs to us - instead, we
- // have to see if any of our windows are children of the foreground window
- fg = GetAncestor (fg, GA_ROOT);
-
- for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
- {
- Win32ComponentPeer* const wp = dynamic_cast (ComponentPeer::getPeer (i));
-
- if (wp != 0 && wp->isInside (fg))
- return true;
- }
-
- return false;
-}
-
-//==============================================================================
-bool AlertWindow::showNativeDialogBox (const String& title,
- const String& bodyText,
- bool isOkCancel)
-{
- return MessageBox (0, bodyText.toWideCharPointer(), title.toWideCharPointer(),
- MB_SETFOREGROUND | (isOkCancel ? MB_OKCANCEL
- : MB_OK)) == IDOK;
-}
-
-
-//==============================================================================
-void Desktop::createMouseInputSources()
-{
- mouseSources.add (new MouseInputSource (0, true));
-}
-
-const Point MouseInputSource::getCurrentMousePosition()
-{
- POINT mousePos;
- GetCursorPos (&mousePos);
- return Point (mousePos.x, mousePos.y);
-}
-
-void Desktop::setMousePosition (const Point& newPosition)
-{
- SetCursorPos (newPosition.getX(), newPosition.getY());
-}
-
-//==============================================================================
-Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage)
-{
- return createSoftwareImage (format, width, height, clearImage);
-}
-
-//==============================================================================
-class ScreenSaverDefeater : public Timer,
- public DeletedAtShutdown
-{
-public:
- ScreenSaverDefeater()
- {
- startTimer (10000);
- timerCallback();
- }
-
- void timerCallback()
- {
- if (Process::isForegroundProcess())
- {
- // simulate a shift key getting pressed..
- INPUT input[2];
- input[0].type = INPUT_KEYBOARD;
- input[0].ki.wVk = VK_SHIFT;
- input[0].ki.dwFlags = 0;
- input[0].ki.dwExtraInfo = 0;
-
- input[1].type = INPUT_KEYBOARD;
- input[1].ki.wVk = VK_SHIFT;
- input[1].ki.dwFlags = KEYEVENTF_KEYUP;
- input[1].ki.dwExtraInfo = 0;
-
- SendInput (2, input, sizeof (INPUT));
- }
- }
-};
-
-static ScreenSaverDefeater* screenSaverDefeater = 0;
-
-void Desktop::setScreenSaverEnabled (const bool isEnabled)
-{
- if (isEnabled)
- deleteAndZero (screenSaverDefeater);
- else if (screenSaverDefeater == 0)
- screenSaverDefeater = new ScreenSaverDefeater();
-}
-
-bool Desktop::isScreenSaverEnabled()
-{
- return screenSaverDefeater == 0;
-}
-
-/* (The code below is the "correct" way to disable the screen saver, but it
- completely fails on winXP when the saver is password-protected...)
-
-static bool juce_screenSaverEnabled = true;
-
-void Desktop::setScreenSaverEnabled (const bool isEnabled) throw()
-{
- juce_screenSaverEnabled = isEnabled;
- SetThreadExecutionState (isEnabled ? ES_CONTINUOUS
- : (ES_DISPLAY_REQUIRED | ES_CONTINUOUS));
-}
-
-bool Desktop::isScreenSaverEnabled() throw()
-{
- return juce_screenSaverEnabled;
-}
-*/
-
-//==============================================================================
-void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool /*allowMenusAndBars*/)
-{
- if (enableOrDisable)
- kioskModeComponent->setBounds (Desktop::getInstance().getMainMonitorArea (false));
-}
-
-//==============================================================================
-static BOOL CALLBACK enumMonitorsProc (HMONITOR, HDC, LPRECT r, LPARAM userInfo)
-{
- Array >* const monitorCoords = (Array >*) userInfo;
- monitorCoords->add (Rectangle (r->left, r->top, r->right - r->left, r->bottom - r->top));
- return TRUE;
-}
-
-void Desktop::getCurrentMonitorPositions (Array >& monitorCoords, const bool clipToWorkArea)
-{
- EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitorCoords);
-
- // make sure the first in the list is the main monitor
- for (int i = 1; i < monitorCoords.size(); ++i)
- if (monitorCoords[i].getX() == 0 && monitorCoords[i].getY() == 0)
- monitorCoords.swap (i, 0);
-
- if (monitorCoords.size() == 0)
- {
- RECT r;
- GetWindowRect (GetDesktopWindow(), &r);
-
- monitorCoords.add (Rectangle (r.left, r.top, r.right - r.left, r.bottom - r.top));
- }
-
- if (clipToWorkArea)
- {
- // clip the main monitor to the active non-taskbar area
- RECT r;
- SystemParametersInfo (SPI_GETWORKAREA, 0, &r, 0);
-
- Rectangle& screen = monitorCoords.getReference (0);
-
- screen.setPosition (jmax (screen.getX(), (int) r.left),
- jmax (screen.getY(), (int) r.top));
-
- screen.setSize (jmin (screen.getRight(), (int) r.right) - screen.getX(),
- jmin (screen.getBottom(), (int) r.bottom) - screen.getY());
- }
-}
-
-//==============================================================================
-const Image juce_createIconForFile (const File& file)
-{
- Image image;
- WORD iconNum = 0;
-
- HICON icon = ExtractAssociatedIcon ((HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle(),
- const_cast (file.getFullPathName().toWideCharPointer()), &iconNum);
-
- if (icon != 0)
- {
- image = IconConverters::createImageFromHICON (icon);
- DestroyIcon (icon);
- }
-
- return image;
-}
-
-//==============================================================================
-void* MouseCursor::createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY)
-{
- const int maxW = GetSystemMetrics (SM_CXCURSOR);
- const int maxH = GetSystemMetrics (SM_CYCURSOR);
-
- Image im (image);
-
- if (im.getWidth() > maxW || im.getHeight() > maxH)
- {
- im = im.rescaled (maxW, maxH);
-
- hotspotX = (hotspotX * maxW) / image.getWidth();
- hotspotY = (hotspotY * maxH) / image.getHeight();
- }
-
- return IconConverters::createHICONFromImage (im, FALSE, hotspotX, hotspotY);
-}
-
-void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool isStandard)
-{
- if (cursorHandle != 0 && ! isStandard)
- DestroyCursor ((HCURSOR) cursorHandle);
-}
-
-enum
-{
- hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor)
-};
-
-void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type)
-{
- LPCTSTR cursorName = IDC_ARROW;
-
- switch (type)
- {
- case NormalCursor: break;
- case NoCursor: return (void*) hiddenMouseCursorHandle;
- case WaitCursor: cursorName = IDC_WAIT; break;
- case IBeamCursor: cursorName = IDC_IBEAM; break;
- case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break;
- case CrosshairCursor: cursorName = IDC_CROSS; break;
- case CopyingCursor: break; // can't seem to find one of these in the win32 list..
-
- case LeftRightResizeCursor:
- case LeftEdgeResizeCursor:
- case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break;
-
- case UpDownResizeCursor:
- case TopEdgeResizeCursor:
- case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break;
-
- case TopLeftCornerResizeCursor:
- case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break;
-
- case TopRightCornerResizeCursor:
- case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break;
-
- case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break;
-
- case DraggingHandCursor:
- {
- static void* dragHandCursor = 0;
-
- if (dragHandCursor == 0)
- {
- static const unsigned char dragHandData[] =
- { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
- 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138,
- 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 };
-
- dragHandCursor = createMouseCursorFromImage (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), 8, 7);
- }
-
- return dragHandCursor;
- }
-
- default:
- jassertfalse; break;
- }
-
- HCURSOR cursorH = LoadCursor (0, cursorName);
-
- if (cursorH == 0)
- cursorH = LoadCursor (0, IDC_ARROW);
-
- return cursorH;
-}
-
-//==============================================================================
-void MouseCursor::showInWindow (ComponentPeer*) const
-{
- HCURSOR c = (HCURSOR) getHandle();
-
- if (c == 0)
- c = LoadCursor (0, IDC_ARROW);
- else if (c == (HCURSOR) hiddenMouseCursorHandle)
- c = 0;
-
- SetCursor (c);
-}
-
-void MouseCursor::showInAllWindows() const
-{
- showInWindow (0);
-}
-
-//==============================================================================
-//==============================================================================
-class JuceDropSource : public ComBaseClassHelper
-{
-public:
- JuceDropSource() {}
-
- HRESULT __stdcall QueryContinueDrag (BOOL escapePressed, DWORD keys)
- {
- if (escapePressed)
- return DRAGDROP_S_CANCEL;
-
- if ((keys & (MK_LBUTTON | MK_RBUTTON)) == 0)
- return DRAGDROP_S_DROP;
-
- return S_OK;
- }
-
- HRESULT __stdcall GiveFeedback (DWORD)
- {
- return DRAGDROP_S_USEDEFAULTCURSORS;
- }
-};
-
-
-class JuceEnumFormatEtc : public ComBaseClassHelper
-{
-public:
- JuceEnumFormatEtc (const FORMATETC* const format_)
- : format (format_),
- index (0)
- {
- }
-
- HRESULT __stdcall Clone (IEnumFORMATETC** result)
- {
- if (result == 0)
- return E_POINTER;
-
- JuceEnumFormatEtc* const newOne = new JuceEnumFormatEtc (format);
- newOne->index = index;
-
- *result = newOne;
- return S_OK;
- }
-
- HRESULT __stdcall Next (ULONG celt, LPFORMATETC lpFormatEtc, ULONG* pceltFetched)
- {
- if (pceltFetched != 0)
- *pceltFetched = 0;
- else if (celt != 1)
- return S_FALSE;
-
- if (index == 0 && celt > 0 && lpFormatEtc != 0)
- {
- copyFormatEtc (lpFormatEtc [0], *format);
- ++index;
-
- if (pceltFetched != 0)
- *pceltFetched = 1;
-
- return S_OK;
- }
-
- return S_FALSE;
- }
-
- HRESULT __stdcall Skip (ULONG celt)
- {
- if (index + (int) celt >= 1)
- return S_FALSE;
-
- index += celt;
- return S_OK;
- }
-
- HRESULT __stdcall Reset()
- {
- index = 0;
- return S_OK;
- }
-
-private:
- const FORMATETC* const format;
- int index;
-
- static void copyFormatEtc (FORMATETC& dest, const FORMATETC& source)
- {
- dest = source;
-
- if (source.ptd != 0)
- {
- dest.ptd = (DVTARGETDEVICE*) CoTaskMemAlloc (sizeof (DVTARGETDEVICE));
- *(dest.ptd) = *(source.ptd);
- }
- }
-
- JUCE_DECLARE_NON_COPYABLE (JuceEnumFormatEtc);
-};
-
-class JuceDataObject : public ComBaseClassHelper
-{
-public:
- JuceDataObject (JuceDropSource* const dropSource_,
- const FORMATETC* const format_,
- const STGMEDIUM* const medium_)
- : dropSource (dropSource_),
- format (format_),
- medium (medium_)
- {
- }
-
- ~JuceDataObject()
- {
- jassert (refCount == 0);
- }
-
- HRESULT __stdcall GetData (FORMATETC* pFormatEtc, STGMEDIUM* pMedium)
- {
- if ((pFormatEtc->tymed & format->tymed) != 0
- && pFormatEtc->cfFormat == format->cfFormat
- && pFormatEtc->dwAspect == format->dwAspect)
- {
- pMedium->tymed = format->tymed;
- pMedium->pUnkForRelease = 0;
-
- if (format->tymed == TYMED_HGLOBAL)
- {
- const SIZE_T len = GlobalSize (medium->hGlobal);
- void* const src = GlobalLock (medium->hGlobal);
- void* const dst = GlobalAlloc (GMEM_FIXED, len);
-
- memcpy (dst, src, len);
-
- GlobalUnlock (medium->hGlobal);
-
- pMedium->hGlobal = dst;
- return S_OK;
- }
- }
-
- return DV_E_FORMATETC;
- }
-
- HRESULT __stdcall QueryGetData (FORMATETC* f)
- {
- if (f == 0)
- return E_INVALIDARG;
-
- if (f->tymed == format->tymed
- && f->cfFormat == format->cfFormat
- && f->dwAspect == format->dwAspect)
- return S_OK;
-
- return DV_E_FORMATETC;
- }
-
- HRESULT __stdcall GetCanonicalFormatEtc (FORMATETC*, FORMATETC* pFormatEtcOut)
- {
- pFormatEtcOut->ptd = 0;
- return E_NOTIMPL;
- }
-
- HRESULT __stdcall EnumFormatEtc (DWORD direction, IEnumFORMATETC** result)
- {
- if (result == 0)
- return E_POINTER;
-
- if (direction == DATADIR_GET)
- {
- *result = new JuceEnumFormatEtc (format);
- return S_OK;
- }
-
- *result = 0;
- return E_NOTIMPL;
- }
-
- HRESULT __stdcall GetDataHere (FORMATETC*, STGMEDIUM*) { return DATA_E_FORMATETC; }
- HRESULT __stdcall SetData (FORMATETC*, STGMEDIUM*, BOOL) { return E_NOTIMPL; }
- HRESULT __stdcall DAdvise (FORMATETC*, DWORD, IAdviseSink*, DWORD*) { return OLE_E_ADVISENOTSUPPORTED; }
- HRESULT __stdcall DUnadvise (DWORD) { return E_NOTIMPL; }
- HRESULT __stdcall EnumDAdvise (IEnumSTATDATA**) { return OLE_E_ADVISENOTSUPPORTED; }
-
-private:
- JuceDropSource* const dropSource;
- const FORMATETC* const format;
- const STGMEDIUM* const medium;
-
- JUCE_DECLARE_NON_COPYABLE (JuceDataObject);
-};
-
-static HDROP createHDrop (const StringArray& fileNames)
-{
- int totalBytes = 0;
- for (int i = fileNames.size(); --i >= 0;)
- totalBytes += CharPointer_UTF16::getBytesRequiredFor (fileNames[i].getCharPointer()) + sizeof (WCHAR);
-
- HDROP hDrop = (HDROP) GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof (DROPFILES) + totalBytes + 4);
-
- if (hDrop != 0)
- {
- LPDROPFILES pDropFiles = (LPDROPFILES) GlobalLock (hDrop);
- pDropFiles->pFiles = sizeof (DROPFILES);
- pDropFiles->fWide = true;
-
- WCHAR* fname = reinterpret_cast (addBytesToPointer (pDropFiles, sizeof (DROPFILES)));
-
- for (int i = 0; i < fileNames.size(); ++i)
- {
- const int bytesWritten = fileNames[i].copyToUTF16 (fname, 2048);
- fname = reinterpret_cast (addBytesToPointer (fname, bytesWritten));
- }
-
- *fname = 0;
-
- GlobalUnlock (hDrop);
- }
-
- return hDrop;
-}
-
-static bool performDragDrop (FORMATETC* const format, STGMEDIUM* const medium, const DWORD whatToDo)
-{
- JuceDropSource* const source = new JuceDropSource();
- JuceDataObject* const data = new JuceDataObject (source, format, medium);
-
- DWORD effect;
- const HRESULT res = DoDragDrop (data, source, whatToDo, &effect);
-
- data->Release();
- source->Release();
-
- return res == DRAGDROP_S_DROP;
-}
-
-bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove)
-{
- FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
- STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
-
- medium.hGlobal = createHDrop (files);
-
- return performDragDrop (&format, &medium, canMove ? (DROPEFFECT_COPY | DROPEFFECT_MOVE)
- : DROPEFFECT_COPY);
-}
-
-bool DragAndDropContainer::performExternalDragDropOfText (const String& text)
-{
- FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
- STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
-
- const int numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
-
- medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2);
- WCHAR* const data = static_cast (GlobalLock (medium.hGlobal));
-
- text.copyToUTF16 (data, numBytes);
- format.cfFormat = CF_UNICODETEXT;
-
- GlobalUnlock (medium.hGlobal);
-
- return performDragDrop (&format, &medium, DROPEFFECT_COPY | DROPEFFECT_MOVE);
-}
-
-#endif
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-11 by Raw Material Software Ltd.
+
+ ------------------------------------------------------------------------------
+
+ JUCE can be redistributed and/or modified under the terms of the GNU General
+ Public License (Version 2), as published by the Free Software Foundation.
+ A copy of the license is included in the JUCE distribution, or can be found
+ online at www.gnu.org/licenses.
+
+ 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.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.rawmaterialsoftware.com/juce for more information.
+
+ ==============================================================================
+*/
+
+// (This file gets included by juce_win32_NativeCode.cpp, rather than being
+// compiled on its own).
+#if JUCE_INCLUDED_FILE
+
+
+//==============================================================================
+#undef GetSystemMetrics // multimon overrides this for some reason and causes a mess..
+
+// these are in the windows SDK, but need to be repeated here for GCC..
+#ifndef GET_APPCOMMAND_LPARAM
+ #define FAPPCOMMAND_MASK 0xF000
+ #define GET_APPCOMMAND_LPARAM(lParam) ((short) (HIWORD (lParam) & ~FAPPCOMMAND_MASK))
+ #define APPCOMMAND_MEDIA_NEXTTRACK 11
+ #define APPCOMMAND_MEDIA_PREVIOUSTRACK 12
+ #define APPCOMMAND_MEDIA_STOP 13
+ #define APPCOMMAND_MEDIA_PLAY_PAUSE 14
+ #define WM_APPCOMMAND 0x0319
+#endif
+
+extern void juce_repeatLastProcessPriority(); // in juce_win32_Threads.cpp
+extern void juce_CheckCurrentlyFocusedTopLevelWindow(); // in juce_TopLevelWindow.cpp
+extern bool juce_IsRunningInWine();
+
+#ifndef ULW_ALPHA
+ #define ULW_ALPHA 0x00000002
+#endif
+
+#ifndef AC_SRC_ALPHA
+ #define AC_SRC_ALPHA 0x01
+#endif
+
+static bool shouldDeactivateTitleBar = true;
+
+#define WM_TRAYNOTIFY WM_USER + 100
+
+//==============================================================================
+typedef BOOL (WINAPI* UpdateLayeredWinFunc) (HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);
+static UpdateLayeredWinFunc updateLayeredWindow = 0;
+
+bool Desktop::canUseSemiTransparentWindows() throw()
+{
+ if (updateLayeredWindow == 0)
+ {
+ if (! juce_IsRunningInWine())
+ {
+ HMODULE user32Mod = GetModuleHandle (_T("user32.dll"));
+ updateLayeredWindow = (UpdateLayeredWinFunc) GetProcAddress (user32Mod, "UpdateLayeredWindow");
+ }
+ }
+
+ return updateLayeredWindow != 0;
+}
+
+Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
+{
+ return upright;
+}
+
+//==============================================================================
+const int extendedKeyModifier = 0x10000;
+
+const int KeyPress::spaceKey = VK_SPACE;
+const int KeyPress::returnKey = VK_RETURN;
+const int KeyPress::escapeKey = VK_ESCAPE;
+const int KeyPress::backspaceKey = VK_BACK;
+const int KeyPress::deleteKey = VK_DELETE | extendedKeyModifier;
+const int KeyPress::insertKey = VK_INSERT | extendedKeyModifier;
+const int KeyPress::tabKey = VK_TAB;
+const int KeyPress::leftKey = VK_LEFT | extendedKeyModifier;
+const int KeyPress::rightKey = VK_RIGHT | extendedKeyModifier;
+const int KeyPress::upKey = VK_UP | extendedKeyModifier;
+const int KeyPress::downKey = VK_DOWN | extendedKeyModifier;
+const int KeyPress::homeKey = VK_HOME | extendedKeyModifier;
+const int KeyPress::endKey = VK_END | extendedKeyModifier;
+const int KeyPress::pageUpKey = VK_PRIOR | extendedKeyModifier;
+const int KeyPress::pageDownKey = VK_NEXT | extendedKeyModifier;
+const int KeyPress::F1Key = VK_F1 | extendedKeyModifier;
+const int KeyPress::F2Key = VK_F2 | extendedKeyModifier;
+const int KeyPress::F3Key = VK_F3 | extendedKeyModifier;
+const int KeyPress::F4Key = VK_F4 | extendedKeyModifier;
+const int KeyPress::F5Key = VK_F5 | extendedKeyModifier;
+const int KeyPress::F6Key = VK_F6 | extendedKeyModifier;
+const int KeyPress::F7Key = VK_F7 | extendedKeyModifier;
+const int KeyPress::F8Key = VK_F8 | extendedKeyModifier;
+const int KeyPress::F9Key = VK_F9 | extendedKeyModifier;
+const int KeyPress::F10Key = VK_F10 | extendedKeyModifier;
+const int KeyPress::F11Key = VK_F11 | extendedKeyModifier;
+const int KeyPress::F12Key = VK_F12 | extendedKeyModifier;
+const int KeyPress::F13Key = VK_F13 | extendedKeyModifier;
+const int KeyPress::F14Key = VK_F14 | extendedKeyModifier;
+const int KeyPress::F15Key = VK_F15 | extendedKeyModifier;
+const int KeyPress::F16Key = VK_F16 | extendedKeyModifier;
+const int KeyPress::numberPad0 = VK_NUMPAD0 | extendedKeyModifier;
+const int KeyPress::numberPad1 = VK_NUMPAD1 | extendedKeyModifier;
+const int KeyPress::numberPad2 = VK_NUMPAD2 | extendedKeyModifier;
+const int KeyPress::numberPad3 = VK_NUMPAD3 | extendedKeyModifier;
+const int KeyPress::numberPad4 = VK_NUMPAD4 | extendedKeyModifier;
+const int KeyPress::numberPad5 = VK_NUMPAD5 | extendedKeyModifier;
+const int KeyPress::numberPad6 = VK_NUMPAD6 | extendedKeyModifier;
+const int KeyPress::numberPad7 = VK_NUMPAD7 | extendedKeyModifier;
+const int KeyPress::numberPad8 = VK_NUMPAD8 | extendedKeyModifier;
+const int KeyPress::numberPad9 = VK_NUMPAD9 | extendedKeyModifier;
+const int KeyPress::numberPadAdd = VK_ADD | extendedKeyModifier;
+const int KeyPress::numberPadSubtract = VK_SUBTRACT | extendedKeyModifier;
+const int KeyPress::numberPadMultiply = VK_MULTIPLY | extendedKeyModifier;
+const int KeyPress::numberPadDivide = VK_DIVIDE | extendedKeyModifier;
+const int KeyPress::numberPadSeparator = VK_SEPARATOR | extendedKeyModifier;
+const int KeyPress::numberPadDecimalPoint = VK_DECIMAL | extendedKeyModifier;
+const int KeyPress::numberPadEquals = 0x92 /*VK_OEM_NEC_EQUAL*/ | extendedKeyModifier;
+const int KeyPress::numberPadDelete = VK_DELETE | extendedKeyModifier;
+const int KeyPress::playKey = 0x30000;
+const int KeyPress::stopKey = 0x30001;
+const int KeyPress::fastForwardKey = 0x30002;
+const int KeyPress::rewindKey = 0x30003;
+
+
+//==============================================================================
+class WindowsBitmapImage : public Image::SharedImage
+{
+public:
+ //==============================================================================
+ WindowsBitmapImage (const Image::PixelFormat format_,
+ const int w, const int h, const bool clearImage)
+ : Image::SharedImage (format_, w, h)
+ {
+ jassert (format_ == Image::RGB || format_ == Image::ARGB);
+
+ pixelStride = (format_ == Image::RGB) ? 3 : 4;
+ lineStride = -((w * pixelStride + 3) & ~3);
+
+ zerostruct (bitmapInfo);
+ bitmapInfo.bV4Size = sizeof (BITMAPV4HEADER);
+ bitmapInfo.bV4Width = w;
+ bitmapInfo.bV4Height = h;
+ bitmapInfo.bV4Planes = 1;
+ bitmapInfo.bV4CSType = 1;
+ bitmapInfo.bV4BitCount = (unsigned short) (pixelStride * 8);
+
+ if (format_ == Image::ARGB)
+ {
+ bitmapInfo.bV4AlphaMask = 0xff000000;
+ bitmapInfo.bV4RedMask = 0xff0000;
+ bitmapInfo.bV4GreenMask = 0xff00;
+ bitmapInfo.bV4BlueMask = 0xff;
+ bitmapInfo.bV4V4Compression = BI_BITFIELDS;
+ }
+ else
+ {
+ bitmapInfo.bV4V4Compression = BI_RGB;
+ }
+
+ HDC dc = GetDC (0);
+ hdc = CreateCompatibleDC (dc);
+ ReleaseDC (0, dc);
+
+ SetMapMode (hdc, MM_TEXT);
+
+ hBitmap = CreateDIBSection (hdc, (BITMAPINFO*) &(bitmapInfo), DIB_RGB_COLORS,
+ (void**) &bitmapData, 0, 0);
+
+ previousBitmap = SelectObject (hdc, hBitmap);
+
+ if (format_ == Image::ARGB && clearImage)
+ zeromem (bitmapData, std::abs (h * lineStride));
+
+ imageData = bitmapData - (lineStride * (h - 1));
+ }
+
+ ~WindowsBitmapImage()
+ {
+ SelectObject (hdc, previousBitmap); // Selecting the previous bitmap before deleting the DC avoids a warning in BoundsChecker
+ DeleteDC (hdc);
+ DeleteObject (hBitmap);
+ }
+
+ Image::ImageType getType() const { return Image::NativeImage; }
+
+ LowLevelGraphicsContext* createLowLevelContext()
+ {
+ return new LowLevelGraphicsSoftwareRenderer (Image (this));
+ }
+
+ void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode /*mode*/)
+ {
+ bitmap.data = imageData + x * pixelStride + y * lineStride;
+ bitmap.pixelFormat = format;
+ bitmap.lineStride = lineStride;
+ bitmap.pixelStride = pixelStride;
+ }
+
+ Image::SharedImage* clone()
+ {
+ WindowsBitmapImage* im = new WindowsBitmapImage (format, width, height, false);
+
+ for (int i = 0; i < height; ++i)
+ memcpy (im->imageData + i * lineStride, imageData + i * lineStride, lineStride);
+
+ return im;
+ }
+
+ void blitToWindow (HWND hwnd, HDC dc, const bool transparent,
+ const int x, const int y,
+ const RectangleList& maskedRegion,
+ const uint8 updateLayeredWindowAlpha) throw()
+ {
+ static HDRAWDIB hdd = 0;
+ static bool needToCreateDrawDib = true;
+
+ if (needToCreateDrawDib)
+ {
+ needToCreateDrawDib = false;
+
+ HDC dc = GetDC (0);
+ const int n = GetDeviceCaps (dc, BITSPIXEL);
+ ReleaseDC (0, dc);
+
+ // only open if we're not palettised
+ if (n > 8)
+ hdd = DrawDibOpen();
+ }
+
+ SetMapMode (dc, MM_TEXT);
+
+ if (transparent)
+ {
+ POINT p, pos;
+ SIZE size;
+
+ RECT windowBounds;
+ GetWindowRect (hwnd, &windowBounds);
+
+ p.x = -x;
+ p.y = -y;
+ pos.x = windowBounds.left;
+ pos.y = windowBounds.top;
+ size.cx = windowBounds.right - windowBounds.left;
+ size.cy = windowBounds.bottom - windowBounds.top;
+
+ BLENDFUNCTION bf;
+ bf.AlphaFormat = AC_SRC_ALPHA;
+ bf.BlendFlags = 0;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.SourceConstantAlpha = updateLayeredWindowAlpha;
+
+ if (! maskedRegion.isEmpty())
+ {
+ for (RectangleList::Iterator i (maskedRegion); i.next();)
+ {
+ const Rectangle& r = *i.getRectangle();
+ ExcludeClipRect (hdc, r.getX(), r.getY(), r.getRight(), r.getBottom());
+ }
+ }
+
+ updateLayeredWindow (hwnd, 0, &pos, &size, hdc, &p, 0, &bf, ULW_ALPHA);
+ }
+ else
+ {
+ int savedDC = 0;
+
+ if (! maskedRegion.isEmpty())
+ {
+ savedDC = SaveDC (dc);
+
+ for (RectangleList::Iterator i (maskedRegion); i.next();)
+ {
+ const Rectangle& r = *i.getRectangle();
+ ExcludeClipRect (dc, r.getX(), r.getY(), r.getRight(), r.getBottom());
+ }
+ }
+
+ if (hdd == 0)
+ {
+ StretchDIBits (dc,
+ x, y, width, height,
+ 0, 0, width, height,
+ bitmapData, (const BITMAPINFO*) &bitmapInfo,
+ DIB_RGB_COLORS, SRCCOPY);
+ }
+ else
+ {
+ DrawDibDraw (hdd, dc, x, y, -1, -1,
+ (BITMAPINFOHEADER*) &bitmapInfo, bitmapData,
+ 0, 0, width, height, 0);
+ }
+
+ if (! maskedRegion.isEmpty())
+ RestoreDC (dc, savedDC);
+ }
+ }
+
+ //==============================================================================
+ HBITMAP hBitmap;
+ HGDIOBJ previousBitmap;
+ BITMAPV4HEADER bitmapInfo;
+ HDC hdc;
+ uint8* bitmapData;
+ int pixelStride, lineStride;
+ uint8* imageData;
+
+private:
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage);
+};
+
+namespace IconConverters
+{
+ const Image createImageFromHBITMAP (HBITMAP bitmap)
+ {
+ Image im;
+
+ if (bitmap != 0)
+ {
+ BITMAP bm;
+
+ if (GetObject (bitmap, sizeof (BITMAP), &bm)
+ && bm.bmWidth > 0 && bm.bmHeight > 0)
+ {
+ HDC tempDC = GetDC (0);
+ HDC dc = CreateCompatibleDC (tempDC);
+ ReleaseDC (0, tempDC);
+
+ SelectObject (dc, bitmap);
+
+ im = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true);
+ Image::BitmapData imageData (im, Image::BitmapData::writeOnly);
+
+ for (int y = bm.bmHeight; --y >= 0;)
+ {
+ for (int x = bm.bmWidth; --x >= 0;)
+ {
+ COLORREF col = GetPixel (dc, x, y);
+
+ imageData.setPixelColour (x, y, Colour ((uint8) GetRValue (col),
+ (uint8) GetGValue (col),
+ (uint8) GetBValue (col)));
+ }
+ }
+
+ DeleteDC (dc);
+ }
+ }
+
+ return im;
+ }
+
+ const Image createImageFromHICON (HICON icon)
+ {
+ ICONINFO info;
+
+ if (GetIconInfo (icon, &info))
+ {
+ Image mask (createImageFromHBITMAP (info.hbmMask));
+ Image image (createImageFromHBITMAP (info.hbmColor));
+
+ if (mask.isValid() && image.isValid())
+ {
+ for (int y = image.getHeight(); --y >= 0;)
+ {
+ for (int x = image.getWidth(); --x >= 0;)
+ {
+ const float brightness = mask.getPixelAt (x, y).getBrightness();
+
+ if (brightness > 0.0f)
+ image.multiplyAlphaAt (x, y, 1.0f - brightness);
+ }
+ }
+
+ return image;
+ }
+ }
+
+ return Image::null;
+ }
+
+ HICON createHICONFromImage (const Image& image, const BOOL isIcon, int hotspotX, int hotspotY)
+ {
+ WindowsBitmapImage* nativeBitmap = new WindowsBitmapImage (Image::ARGB, image.getWidth(), image.getHeight(), true);
+ Image bitmap (nativeBitmap);
+
+ {
+ Graphics g (bitmap);
+ g.drawImageAt (image, 0, 0);
+ }
+
+ HBITMAP mask = CreateBitmap (image.getWidth(), image.getHeight(), 1, 1, 0);
+
+ ICONINFO info;
+ info.fIcon = isIcon;
+ info.xHotspot = hotspotX;
+ info.yHotspot = hotspotY;
+ info.hbmMask = mask;
+ info.hbmColor = nativeBitmap->hBitmap;
+
+ HICON hi = CreateIconIndirect (&info);
+ DeleteObject (mask);
+ return hi;
+ }
+}
+
+//==============================================================================
+long improbableWindowNumber = 0xf965aa01; // also referenced by messaging.cpp
+
+
+//==============================================================================
+class Win32ComponentPeer : public ComponentPeer
+{
+public:
+ enum RenderingEngineType
+ {
+ softwareRenderingEngine = 0,
+ direct2DRenderingEngine
+ };
+
+ //==============================================================================
+ Win32ComponentPeer (Component* const component,
+ const int windowStyleFlags,
+ HWND parentToAddTo_)
+ : ComponentPeer (component, windowStyleFlags),
+ dontRepaint (false),
+ currentRenderingEngine (softwareRenderingEngine),
+ fullScreen (false),
+ isDragging (false),
+ isMouseOver (false),
+ hasCreatedCaret (false),
+ constrainerIsResizing (false),
+ currentWindowIcon (0),
+ dropTarget (0),
+ parentToAddTo (parentToAddTo_),
+ updateLayeredWindowAlpha (255)
+ {
+ callFunctionIfNotLocked (&createWindowCallback, this);
+
+ setTitle (component->getName());
+
+ if ((windowStyleFlags & windowHasDropShadow) != 0
+ && Desktop::canUseSemiTransparentWindows())
+ {
+ shadower = component->getLookAndFeel().createDropShadowerForComponent (component);
+
+ if (shadower != 0)
+ shadower->setOwner (component);
+ }
+ }
+
+ ~Win32ComponentPeer()
+ {
+ setTaskBarIcon (Image());
+ shadower = 0;
+
+ // do this before the next bit to avoid messages arriving for this window
+ // before it's destroyed
+ SetWindowLongPtr (hwnd, GWLP_USERDATA, 0);
+
+ callFunctionIfNotLocked (&destroyWindowCallback, (void*) hwnd);
+
+ if (currentWindowIcon != 0)
+ DestroyIcon (currentWindowIcon);
+
+ if (dropTarget != 0)
+ {
+ dropTarget->Release();
+ dropTarget = 0;
+ }
+
+ #if JUCE_DIRECT2D
+ direct2DContext = 0;
+ #endif
+ }
+
+ //==============================================================================
+ void* getNativeHandle() const
+ {
+ return hwnd;
+ }
+
+ void setVisible (bool shouldBeVisible)
+ {
+ ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
+
+ if (shouldBeVisible)
+ InvalidateRect (hwnd, 0, 0);
+ else
+ lastPaintTime = 0;
+ }
+
+ void setTitle (const String& title)
+ {
+ SetWindowText (hwnd, title.toWideCharPointer());
+ }
+
+ void setPosition (int x, int y)
+ {
+ offsetWithinParent (x, y);
+ SetWindowPos (hwnd, 0,
+ x - windowBorder.getLeft(),
+ y - windowBorder.getTop(),
+ 0, 0,
+ SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+ }
+
+ void repaintNowIfTransparent()
+ {
+ if (isUsingUpdateLayeredWindow() && lastPaintTime > 0 && Time::getMillisecondCounter() > lastPaintTime + 30)
+ handlePaintMessage();
+ }
+
+ void updateBorderSize()
+ {
+ WINDOWINFO info;
+ info.cbSize = sizeof (info);
+
+ if (GetWindowInfo (hwnd, &info))
+ {
+ windowBorder = BorderSize (info.rcClient.top - info.rcWindow.top,
+ info.rcClient.left - info.rcWindow.left,
+ info.rcWindow.bottom - info.rcClient.bottom,
+ info.rcWindow.right - info.rcClient.right);
+ }
+
+ #if JUCE_DIRECT2D
+ if (direct2DContext != 0)
+ direct2DContext->resized();
+ #endif
+ }
+
+ void setSize (int w, int h)
+ {
+ SetWindowPos (hwnd, 0, 0, 0,
+ w + windowBorder.getLeftAndRight(),
+ h + windowBorder.getTopAndBottom(),
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+
+ updateBorderSize();
+
+ repaintNowIfTransparent();
+ }
+
+ void setBounds (int x, int y, int w, int h, bool isNowFullScreen)
+ {
+ fullScreen = isNowFullScreen;
+ offsetWithinParent (x, y);
+
+ SetWindowPos (hwnd, 0,
+ x - windowBorder.getLeft(),
+ y - windowBorder.getTop(),
+ w + windowBorder.getLeftAndRight(),
+ h + windowBorder.getTopAndBottom(),
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+
+ updateBorderSize();
+
+ repaintNowIfTransparent();
+ }
+
+ const Rectangle getBounds() const
+ {
+ RECT r;
+ GetWindowRect (hwnd, &r);
+
+ Rectangle bounds (r.left, r.top, r.right - r.left, r.bottom - r.top);
+
+ HWND parentH = GetParent (hwnd);
+ if (parentH != 0)
+ {
+ GetWindowRect (parentH, &r);
+ bounds.translate (-r.left, -r.top);
+ }
+
+ return windowBorder.subtractedFrom (bounds);
+ }
+
+ const Point getScreenPosition() const
+ {
+ RECT r;
+ GetWindowRect (hwnd, &r);
+ return Point (r.left + windowBorder.getLeft(),
+ r.top + windowBorder.getTop());
+ }
+
+ const Point localToGlobal (const Point& relativePosition)
+ {
+ return relativePosition + getScreenPosition();
+ }
+
+ const Point globalToLocal (const Point& screenPosition)
+ {
+ return screenPosition - getScreenPosition();
+ }
+
+ void setAlpha (float newAlpha)
+ {
+ const uint8 intAlpha = (uint8) jlimit (0, 255, (int) (newAlpha * 255.0f));
+
+ if (component->isOpaque())
+ {
+ if (newAlpha < 1.0f)
+ {
+ SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+ SetLayeredWindowAttributes (hwnd, RGB (0, 0, 0), intAlpha, LWA_ALPHA);
+ }
+ else
+ {
+ SetWindowLong (hwnd, GWL_EXSTYLE, GetWindowLong (hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
+ RedrawWindow (hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
+ }
+ }
+ else
+ {
+ updateLayeredWindowAlpha = intAlpha;
+ component->repaint();
+ }
+ }
+
+ void setMinimised (bool shouldBeMinimised)
+ {
+ if (shouldBeMinimised != isMinimised())
+ ShowWindow (hwnd, shouldBeMinimised ? SW_MINIMIZE : SW_SHOWNORMAL);
+ }
+
+ bool isMinimised() const
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof (WINDOWPLACEMENT);
+ GetWindowPlacement (hwnd, &wp);
+
+ return wp.showCmd == SW_SHOWMINIMIZED;
+ }
+
+ void setFullScreen (bool shouldBeFullScreen)
+ {
+ setMinimised (false);
+
+ if (fullScreen != shouldBeFullScreen)
+ {
+ fullScreen = shouldBeFullScreen;
+ const WeakReference deletionChecker (component);
+
+ if (! fullScreen)
+ {
+ const Rectangle boundsCopy (lastNonFullscreenBounds);
+
+ if (hasTitleBar())
+ ShowWindow (hwnd, SW_SHOWNORMAL);
+
+ if (! boundsCopy.isEmpty())
+ {
+ setBounds (boundsCopy.getX(),
+ boundsCopy.getY(),
+ boundsCopy.getWidth(),
+ boundsCopy.getHeight(),
+ false);
+ }
+ }
+ else
+ {
+ if (hasTitleBar())
+ ShowWindow (hwnd, SW_SHOWMAXIMIZED);
+ else
+ SendMessageW (hwnd, WM_SETTINGCHANGE, 0, 0);
+ }
+
+ if (deletionChecker != 0)
+ handleMovedOrResized();
+ }
+ }
+
+ bool isFullScreen() const
+ {
+ if (! hasTitleBar())
+ return fullScreen;
+
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof (wp);
+ GetWindowPlacement (hwnd, &wp);
+
+ return wp.showCmd == SW_SHOWMAXIMIZED;
+ }
+
+ bool contains (const Point& position, bool trueIfInAChildWindow) const
+ {
+ if (! (isPositiveAndBelow (position.getX(), component->getWidth())
+ && isPositiveAndBelow (position.getY(), component->getHeight())))
+ return false;
+
+ RECT r;
+ GetWindowRect (hwnd, &r);
+
+ POINT p;
+ p.x = position.getX() + r.left + windowBorder.getLeft();
+ p.y = position.getY() + r.top + windowBorder.getTop();
+
+ HWND w = WindowFromPoint (p);
+ return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0));
+ }
+
+ const BorderSize getFrameSize() const
+ {
+ return windowBorder;
+ }
+
+ bool setAlwaysOnTop (bool alwaysOnTop)
+ {
+ const bool oldDeactivate = shouldDeactivateTitleBar;
+ shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
+
+ SetWindowPos (hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
+ 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+
+ shouldDeactivateTitleBar = oldDeactivate;
+
+ if (shadower != 0)
+ shadower->componentBroughtToFront (*component);
+
+ return true;
+ }
+
+ void toFront (bool makeActive)
+ {
+ setMinimised (false);
+
+ const bool oldDeactivate = shouldDeactivateTitleBar;
+ shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
+
+ callFunctionIfNotLocked (makeActive ? &toFrontCallback1 : &toFrontCallback2, hwnd);
+
+ shouldDeactivateTitleBar = oldDeactivate;
+
+ if (! makeActive)
+ {
+ // in this case a broughttofront call won't have occured, so do it now..
+ handleBroughtToFront();
+ }
+ }
+
+ void toBehind (ComponentPeer* other)
+ {
+ Win32ComponentPeer* const otherPeer = dynamic_cast (other);
+
+ jassert (otherPeer != 0); // wrong type of window?
+
+ if (otherPeer != 0)
+ {
+ setMinimised (false);
+
+ // must be careful not to try to put a topmost window behind a normal one, or win32
+ // promotes the normal one to be topmost!
+ if (getComponent()->isAlwaysOnTop() == otherPeer->getComponent()->isAlwaysOnTop())
+ SetWindowPos (hwnd, otherPeer->hwnd, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+ else if (otherPeer->getComponent()->isAlwaysOnTop())
+ SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+ }
+ }
+
+ bool isFocused() const
+ {
+ return callFunctionIfNotLocked (&getFocusCallback, 0) == (void*) hwnd;
+ }
+
+ void grabFocus()
+ {
+ const bool oldDeactivate = shouldDeactivateTitleBar;
+ shouldDeactivateTitleBar = ((styleFlags & windowIsTemporary) == 0);
+
+ callFunctionIfNotLocked (&setFocusCallback, hwnd);
+
+ shouldDeactivateTitleBar = oldDeactivate;
+ }
+
+ void textInputRequired (const Point&)
+ {
+ if (! hasCreatedCaret)
+ {
+ hasCreatedCaret = true;
+ CreateCaret (hwnd, (HBITMAP) 1, 0, 0);
+ }
+
+ ShowCaret (hwnd);
+ SetCaretPos (0, 0);
+ }
+
+ void dismissPendingTextInput()
+ {
+ imeHandler.handleSetContext (hwnd, false);
+ }
+
+ void repaint (const Rectangle& area)
+ {
+ const RECT r = { area.getX(), area.getY(), area.getRight(), area.getBottom() };
+ InvalidateRect (hwnd, &r, FALSE);
+ }
+
+ void performAnyPendingRepaintsNow()
+ {
+ MSG m;
+ if (component->isVisible()
+ && (PeekMessage (&m, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE) || isUsingUpdateLayeredWindow()))
+ handlePaintMessage();
+ }
+
+ //==============================================================================
+ static Win32ComponentPeer* getOwnerOfWindow (HWND h) throw()
+ {
+ if (h != 0 && GetWindowLongPtr (h, GWLP_USERDATA) == improbableWindowNumber)
+ return (Win32ComponentPeer*) (pointer_sized_int) GetWindowLongPtr (h, 8);
+
+ return 0;
+ }
+
+ //==============================================================================
+ void setTaskBarIcon (const Image& image)
+ {
+ if (image.isValid())
+ {
+ HICON hicon = IconConverters::createHICONFromImage (image, TRUE, 0, 0);
+
+ if (taskBarIcon == 0)
+ {
+ taskBarIcon = new NOTIFYICONDATA();
+ zeromem (taskBarIcon, sizeof (NOTIFYICONDATA));
+ taskBarIcon->cbSize = sizeof (NOTIFYICONDATA);
+ taskBarIcon->hWnd = (HWND) hwnd;
+ taskBarIcon->uID = (int) (pointer_sized_int) hwnd;
+ taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+ taskBarIcon->uCallbackMessage = WM_TRAYNOTIFY;
+ taskBarIcon->hIcon = hicon;
+ taskBarIcon->szTip[0] = 0;
+
+ Shell_NotifyIcon (NIM_ADD, taskBarIcon);
+ }
+ else
+ {
+ HICON oldIcon = taskBarIcon->hIcon;
+
+ taskBarIcon->hIcon = hicon;
+ taskBarIcon->uFlags = NIF_ICON;
+ Shell_NotifyIcon (NIM_MODIFY, taskBarIcon);
+
+ DestroyIcon (oldIcon);
+ }
+ }
+ else if (taskBarIcon != 0)
+ {
+ taskBarIcon->uFlags = 0;
+ Shell_NotifyIcon (NIM_DELETE, taskBarIcon);
+ DestroyIcon (taskBarIcon->hIcon);
+ taskBarIcon = 0;
+ }
+ }
+
+ void setTaskBarIconToolTip (const String& toolTip) const
+ {
+ if (taskBarIcon != 0)
+ {
+ taskBarIcon->uFlags = NIF_TIP;
+ toolTip.copyToUTF16 (taskBarIcon->szTip, sizeof (taskBarIcon->szTip) - 1);
+ Shell_NotifyIcon (NIM_MODIFY, taskBarIcon);
+ }
+ }
+
+ void handleTaskBarEvent (const LPARAM lParam)
+ {
+ if (component->isCurrentlyBlockedByAnotherModalComponent())
+ {
+ if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN
+ || lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK)
+ {
+ Component* const current = Component::getCurrentlyModalComponent();
+
+ if (current != 0)
+ current->inputAttemptWhenModal();
+ }
+ }
+ else
+ {
+ ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime());
+
+ if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK)
+ eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier);
+ else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK)
+ eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier);
+ else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
+ eventMods = eventMods.withoutMouseButtons();
+
+ const MouseEvent e (Desktop::getInstance().getMainMouseSource(),
+ Point(), eventMods, component, component, Time (getMouseEventTime()),
+ Point(), Time (getMouseEventTime()), 1, false);
+
+ if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
+ {
+ SetFocus (hwnd);
+ SetForegroundWindow (hwnd);
+ component->mouseDown (e);
+ }
+ else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
+ {
+ component->mouseUp (e);
+ }
+ else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_LBUTTONDBLCLK)
+ {
+ component->mouseDoubleClick (e);
+ }
+ else if (lParam == WM_MOUSEMOVE)
+ {
+ component->mouseMove (e);
+ }
+ }
+ }
+
+ //==============================================================================
+ bool isInside (HWND h) const throw()
+ {
+ return GetAncestor (hwnd, GA_ROOT) == h;
+ }
+
+ //==============================================================================
+ static bool isKeyDown (const int key) throw() { return (GetAsyncKeyState (key) & 0x8000) != 0; }
+
+ static void updateKeyModifiers() throw()
+ {
+ int keyMods = 0;
+ if (isKeyDown (VK_SHIFT)) keyMods |= ModifierKeys::shiftModifier;
+ if (isKeyDown (VK_CONTROL)) keyMods |= ModifierKeys::ctrlModifier;
+ if (isKeyDown (VK_MENU)) keyMods |= ModifierKeys::altModifier;
+ if (isKeyDown (VK_RMENU)) keyMods &= ~(ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
+
+ currentModifiers = currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
+ }
+
+ static void updateModifiersFromWParam (const WPARAM wParam)
+ {
+ int mouseMods = 0;
+ if (wParam & MK_LBUTTON) mouseMods |= ModifierKeys::leftButtonModifier;
+ if (wParam & MK_RBUTTON) mouseMods |= ModifierKeys::rightButtonModifier;
+ if (wParam & MK_MBUTTON) mouseMods |= ModifierKeys::middleButtonModifier;
+
+ currentModifiers = currentModifiers.withoutMouseButtons().withFlags (mouseMods);
+ updateKeyModifiers();
+ }
+
+ static int64 getMouseEventTime()
+ {
+ static int64 eventTimeOffset = 0;
+ static DWORD lastMessageTime = 0;
+ const DWORD thisMessageTime = GetMessageTime();
+
+ if (thisMessageTime < lastMessageTime || lastMessageTime == 0)
+ {
+ lastMessageTime = thisMessageTime;
+ eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
+ }
+
+ return eventTimeOffset + thisMessageTime;
+ }
+
+ //==============================================================================
+ bool dontRepaint;
+
+ static ModifierKeys currentModifiers;
+ static ModifierKeys modifiersAtLastCallback;
+
+private:
+ HWND hwnd, parentToAddTo;
+ ScopedPointer shadower;
+ RenderingEngineType currentRenderingEngine;
+ #if JUCE_DIRECT2D
+ ScopedPointer direct2DContext;
+ #endif
+ bool fullScreen, isDragging, isMouseOver, hasCreatedCaret, constrainerIsResizing;
+ BorderSize windowBorder;
+ HICON currentWindowIcon;
+ ScopedPointer taskBarIcon;
+ IDropTarget* dropTarget;
+ uint8 updateLayeredWindowAlpha;
+
+ //==============================================================================
+ class TemporaryImage : public Timer
+ {
+ public:
+ TemporaryImage() {}
+
+ const Image& getImage (const bool transparent, const int w, const int h)
+ {
+ const Image::PixelFormat format = transparent ? Image::ARGB : Image::RGB;
+
+ if ((! image.isValid()) || image.getWidth() < w || image.getHeight() < h || image.getFormat() != format)
+ image = Image (new WindowsBitmapImage (format, (w + 31) & ~31, (h + 31) & ~31, false));
+
+ startTimer (3000);
+ return image;
+ }
+
+ void timerCallback()
+ {
+ stopTimer();
+ image = Image::null;
+ }
+
+ private:
+ Image image;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryImage);
+ };
+
+ TemporaryImage offscreenImageGenerator;
+
+ //==============================================================================
+ class WindowClassHolder : public DeletedAtShutdown
+ {
+ public:
+ WindowClassHolder()
+ {
+ // this name has to be different for each app/dll instance because otherwise poor old Win32 can
+ // get a bit confused (even despite it not being a process-global window class).
+ String windowClassName ("JUCE_");
+ windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
+
+ HINSTANCE moduleHandle = (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle();
+
+ TCHAR moduleFile [1024] = { 0 };
+ GetModuleFileName (moduleHandle, moduleFile, 1024);
+ WORD iconNum = 0;
+
+ WNDCLASSEX wcex = { 0 };
+ wcex.cbSize = sizeof (wcex);
+ wcex.style = CS_OWNDC;
+ wcex.lpfnWndProc = (WNDPROC) windowProc;
+ wcex.lpszClassName = windowClassName.toWideCharPointer();
+ wcex.cbWndExtra = 32;
+ wcex.hInstance = moduleHandle;
+ wcex.hIcon = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
+ iconNum = 1;
+ wcex.hIconSm = ExtractAssociatedIcon (moduleHandle, moduleFile, &iconNum);
+
+ atom = RegisterClassEx (&wcex);
+ jassert (atom != 0);
+ }
+
+ ~WindowClassHolder()
+ {
+ if (ComponentPeer::getNumPeers() == 0)
+ UnregisterClass (getWindowClassName(), (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle());
+
+ clearSingletonInstance();
+ }
+
+ LPCTSTR getWindowClassName() const throw() { return (LPCTSTR) MAKELONG (atom, 0); }
+
+ juce_DeclareSingleton_SingleThreaded_Minimal (WindowClassHolder);
+
+ private:
+ ATOM atom;
+
+ JUCE_DECLARE_NON_COPYABLE (WindowClassHolder);
+ };
+
+ //==============================================================================
+ static void* createWindowCallback (void* userData)
+ {
+ static_cast (userData)->createWindow();
+ return 0;
+ }
+
+ void createWindow()
+ {
+ DWORD exstyle = WS_EX_ACCEPTFILES;
+ DWORD type = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+
+ if (hasTitleBar())
+ {
+ type |= WS_OVERLAPPED;
+
+ if ((styleFlags & windowHasCloseButton) != 0)
+ {
+ type |= WS_SYSMENU;
+ }
+ else
+ {
+ // annoyingly, windows won't let you have a min/max button without a close button
+ jassert ((styleFlags & (windowHasMinimiseButton | windowHasMaximiseButton)) == 0);
+ }
+
+ if ((styleFlags & windowIsResizable) != 0)
+ type |= WS_THICKFRAME;
+ }
+ else if (parentToAddTo != 0)
+ {
+ type |= WS_CHILD;
+ }
+ else
+ {
+ type |= WS_POPUP | WS_SYSMENU;
+ }
+
+ if ((styleFlags & windowAppearsOnTaskbar) == 0)
+ exstyle |= WS_EX_TOOLWINDOW;
+ else
+ exstyle |= WS_EX_APPWINDOW;
+
+ if ((styleFlags & windowHasMinimiseButton) != 0)
+ type |= WS_MINIMIZEBOX;
+
+ if ((styleFlags & windowHasMaximiseButton) != 0)
+ type |= WS_MAXIMIZEBOX;
+
+ if ((styleFlags & windowIgnoresMouseClicks) != 0)
+ exstyle |= WS_EX_TRANSPARENT;
+
+ if ((styleFlags & windowIsSemiTransparent) != 0 && Desktop::canUseSemiTransparentWindows())
+ exstyle |= WS_EX_LAYERED;
+
+ hwnd = CreateWindowEx (exstyle, WindowClassHolder::getInstance()->getWindowClassName(),
+ L"", type, 0, 0, 0, 0, parentToAddTo, 0,
+ (HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle(), 0);
+
+ #if JUCE_DIRECT2D
+ setCurrentRenderingEngine (1);
+ #endif
+
+ if (hwnd != 0)
+ {
+ SetWindowLongPtr (hwnd, 0, 0);
+ SetWindowLongPtr (hwnd, 8, (LONG_PTR) this);
+ SetWindowLongPtr (hwnd, GWLP_USERDATA, improbableWindowNumber);
+
+ if (dropTarget == 0)
+ dropTarget = new JuceDropTarget (this);
+
+ RegisterDragDrop (hwnd, dropTarget);
+
+ updateBorderSize();
+
+ // Calling this function here is (for some reason) necessary to make Windows
+ // correctly enable the menu items that we specify in the wm_initmenu message.
+ GetSystemMenu (hwnd, false);
+
+ const float alpha = component->getAlpha();
+ if (alpha < 1.0f)
+ setAlpha (alpha);
+ }
+ else
+ {
+ jassertfalse;
+ }
+ }
+
+ static void* destroyWindowCallback (void* handle)
+ {
+ RevokeDragDrop ((HWND) handle);
+ DestroyWindow ((HWND) handle);
+ return 0;
+ }
+
+ static void* toFrontCallback1 (void* h)
+ {
+ SetForegroundWindow ((HWND) h);
+ return 0;
+ }
+
+ static void* toFrontCallback2 (void* h)
+ {
+ SetWindowPos ((HWND) h, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+ return 0;
+ }
+
+ static void* setFocusCallback (void* h)
+ {
+ SetFocus ((HWND) h);
+ return 0;
+ }
+
+ static void* getFocusCallback (void*)
+ {
+ return GetFocus();
+ }
+
+ void offsetWithinParent (int& x, int& y) const
+ {
+ if (isUsingUpdateLayeredWindow())
+ {
+ HWND parentHwnd = GetParent (hwnd);
+
+ if (parentHwnd != 0)
+ {
+ RECT parentRect;
+ GetWindowRect (parentHwnd, &parentRect);
+ x += parentRect.left;
+ y += parentRect.top;
+ }
+ }
+ }
+
+ bool isUsingUpdateLayeredWindow() const
+ {
+ return ! component->isOpaque();
+ }
+
+ inline bool hasTitleBar() const throw() { return (styleFlags & windowHasTitleBar) != 0; }
+
+
+ void setIcon (const Image& newIcon)
+ {
+ HICON hicon = IconConverters::createHICONFromImage (newIcon, TRUE, 0, 0);
+
+ if (hicon != 0)
+ {
+ SendMessage (hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
+ SendMessage (hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
+
+ if (currentWindowIcon != 0)
+ DestroyIcon (currentWindowIcon);
+
+ currentWindowIcon = hicon;
+ }
+ }
+
+ //==============================================================================
+ void handlePaintMessage()
+ {
+ #if JUCE_DIRECT2D
+ if (direct2DContext != 0)
+ {
+ RECT r;
+
+ if (GetUpdateRect (hwnd, &r, false))
+ {
+ direct2DContext->start();
+ direct2DContext->clipToRectangle (Rectangle (r.left, r.top, r.right - r.left, r.bottom - r.top));
+ handlePaint (*direct2DContext);
+ direct2DContext->end();
+ }
+ }
+ else
+ #endif
+
+ {
+ HRGN rgn = CreateRectRgn (0, 0, 0, 0);
+ const int regionType = GetUpdateRgn (hwnd, rgn, false);
+
+ PAINTSTRUCT paintStruct;
+ HDC dc = BeginPaint (hwnd, &paintStruct); // Note this can immediately generate a WM_NCPAINT
+ // message and become re-entrant, but that's OK
+
+ // if something in a paint handler calls, e.g. a message box, this can become reentrant and
+ // corrupt the image it's using to paint into, so do a check here.
+ static bool reentrant = false;
+ if (reentrant)
+ {
+ DeleteObject (rgn);
+ EndPaint (hwnd, &paintStruct);
+ return;
+ }
+
+ const ScopedValueSetter setter (reentrant, true, false);
+
+ // this is the rectangle to update..
+ int x = paintStruct.rcPaint.left;
+ int y = paintStruct.rcPaint.top;
+ int w = paintStruct.rcPaint.right - x;
+ int h = paintStruct.rcPaint.bottom - y;
+
+ const bool transparent = isUsingUpdateLayeredWindow();
+
+ if (transparent)
+ {
+ // it's not possible to have a transparent window with a title bar at the moment!
+ jassert (! hasTitleBar());
+
+ RECT r;
+ GetWindowRect (hwnd, &r);
+ x = y = 0;
+ w = r.right - r.left;
+ h = r.bottom - r.top;
+ }
+
+ if (w > 0 && h > 0)
+ {
+ clearMaskedRegion();
+
+ Image offscreenImage (offscreenImageGenerator.getImage (transparent, w, h));
+
+ RectangleList contextClip;
+ const Rectangle clipBounds (0, 0, w, h);
+
+ bool needToPaintAll = true;
+
+ if (regionType == COMPLEXREGION && ! transparent)
+ {
+ HRGN clipRgn = CreateRectRgnIndirect (&paintStruct.rcPaint);
+ CombineRgn (rgn, rgn, clipRgn, RGN_AND);
+ DeleteObject (clipRgn);
+
+ char rgnData [8192];
+ const DWORD res = GetRegionData (rgn, sizeof (rgnData), (RGNDATA*) rgnData);
+
+ if (res > 0 && res <= sizeof (rgnData))
+ {
+ const RGNDATAHEADER* const hdr = &(((const RGNDATA*) rgnData)->rdh);
+
+ if (hdr->iType == RDH_RECTANGLES
+ && hdr->rcBound.right - hdr->rcBound.left >= w
+ && hdr->rcBound.bottom - hdr->rcBound.top >= h)
+ {
+ needToPaintAll = false;
+
+ const RECT* rects = (const RECT*) (rgnData + sizeof (RGNDATAHEADER));
+ int num = ((RGNDATA*) rgnData)->rdh.nCount;
+
+ while (--num >= 0)
+ {
+ if (rects->right <= x + w && rects->bottom <= y + h)
+ {
+ const int cx = jmax (x, (int) rects->left);
+ contextClip.addWithoutMerging (Rectangle (cx - x, rects->top - y, rects->right - cx, rects->bottom - rects->top)
+ .getIntersection (clipBounds));
+ }
+ else
+ {
+ needToPaintAll = true;
+ break;
+ }
+
+ ++rects;
+ }
+ }
+ }
+ }
+
+ if (needToPaintAll)
+ {
+ contextClip.clear();
+ contextClip.addWithoutMerging (Rectangle (w, h));
+ }
+
+ if (transparent)
+ {
+ RectangleList::Iterator i (contextClip);
+
+ while (i.next())
+ offscreenImage.clear (*i.getRectangle());
+ }
+
+ // if the component's not opaque, this won't draw properly unless the platform can support this
+ jassert (Desktop::canUseSemiTransparentWindows() || component->isOpaque());
+
+ updateCurrentModifiers();
+
+ LowLevelGraphicsSoftwareRenderer context (offscreenImage, -x, -y, contextClip);
+ handlePaint (context);
+
+ if (! dontRepaint)
+ static_cast (offscreenImage.getSharedImage())
+ ->blitToWindow (hwnd, dc, transparent, x, y, maskedRegion, updateLayeredWindowAlpha);
+ }
+
+ DeleteObject (rgn);
+ EndPaint (hwnd, &paintStruct);
+ }
+
+ #ifndef JUCE_GCC
+ _fpreset(); // because some graphics cards can unmask FP exceptions
+ #endif
+
+ lastPaintTime = Time::getMillisecondCounter();
+ }
+
+ //==============================================================================
+ void doMouseEvent (const Point& position)
+ {
+ handleMouseEvent (0, position, currentModifiers, getMouseEventTime());
+ }
+
+ const StringArray getAvailableRenderingEngines()
+ {
+ StringArray s (ComponentPeer::getAvailableRenderingEngines());
+
+ #if JUCE_DIRECT2D
+ if (SystemStats::getOperatingSystemType() >= SystemStats::Windows7)
+ s.add ("Direct2D");
+ #endif
+
+ return s;
+ }
+
+ int getCurrentRenderingEngine() const { return currentRenderingEngine; }
+
+ #if JUCE_DIRECT2D
+ void updateDirect2DContext()
+ {
+ if (currentRenderingEngine != direct2DRenderingEngine)
+ direct2DContext = 0;
+ else if (direct2DContext == 0)
+ direct2DContext = new Direct2DLowLevelGraphicsContext (hwnd);
+ }
+ #endif
+
+ void setCurrentRenderingEngine (int index)
+ {
+ (void) index;
+
+ #if JUCE_DIRECT2D
+ if (getAvailableRenderingEngines().size() > 1)
+ {
+ currentRenderingEngine = index == 1 ? direct2DRenderingEngine : softwareRenderingEngine;
+ updateDirect2DContext();
+ repaint (component->getLocalBounds());
+ }
+ #endif
+ }
+
+ void doMouseMove (const Point& position)
+ {
+ if (! isMouseOver)
+ {
+ isMouseOver = true;
+ updateKeyModifiers();
+
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof (tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hwnd;
+ tme.dwHoverTime = 0;
+
+ if (! TrackMouseEvent (&tme))
+ jassertfalse;
+
+ Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
+ }
+ else if (! isDragging)
+ {
+ if (! contains (position, false))
+ return;
+ }
+
+ // (Throttling the incoming queue of mouse-events seems to still be required in XP..)
+ static uint32 lastMouseTime = 0;
+ const uint32 now = Time::getMillisecondCounter();
+ const int maxMouseMovesPerSecond = 60;
+
+ if (now > lastMouseTime + 1000 / maxMouseMovesPerSecond)
+ {
+ lastMouseTime = now;
+ doMouseEvent (position);
+ }
+ }
+
+ void doMouseDown (const Point& position, const WPARAM wParam)
+ {
+ if (GetCapture() != hwnd)
+ SetCapture (hwnd);
+
+ doMouseMove (position);
+
+ updateModifiersFromWParam (wParam);
+ isDragging = true;
+
+ doMouseEvent (position);
+ }
+
+ void doMouseUp (const Point& position, const WPARAM wParam)
+ {
+ updateModifiersFromWParam (wParam);
+ isDragging = false;
+
+ // release the mouse capture if the user has released all buttons
+ if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) == 0 && hwnd == GetCapture())
+ ReleaseCapture();
+
+ doMouseEvent (position);
+ }
+
+ void doCaptureChanged()
+ {
+ if (constrainerIsResizing)
+ {
+ if (constrainer != 0)
+ constrainer->resizeEnd();
+
+ constrainerIsResizing = false;
+ }
+
+ if (isDragging)
+ doMouseUp (getCurrentMousePos(), (WPARAM) 0);
+ }
+
+ void doMouseExit()
+ {
+ isMouseOver = false;
+ doMouseEvent (getCurrentMousePos());
+ }
+
+ void doMouseWheel (const Point& position, const WPARAM wParam, const bool isVertical)
+ {
+ updateKeyModifiers();
+
+ const float amount = jlimit (-1000.0f, 1000.0f, 0.75f * (short) HIWORD (wParam));
+
+ handleMouseWheel (0, position, getMouseEventTime(),
+ isVertical ? 0.0f : amount,
+ isVertical ? amount : 0.0f);
+ }
+
+ //==============================================================================
+ void sendModifierKeyChangeIfNeeded()
+ {
+ if (modifiersAtLastCallback != currentModifiers)
+ {
+ modifiersAtLastCallback = currentModifiers;
+ handleModifierKeysChange();
+ }
+ }
+
+ bool doKeyUp (const WPARAM key)
+ {
+ updateKeyModifiers();
+
+ switch (key)
+ {
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ case VK_CAPITAL:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_APPS:
+ case VK_NUMLOCK:
+ case VK_SCROLL:
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ case VK_LCONTROL:
+ case VK_LMENU:
+ case VK_RCONTROL:
+ case VK_RMENU:
+ sendModifierKeyChangeIfNeeded();
+ }
+
+ return handleKeyUpOrDown (false)
+ || Component::getCurrentlyModalComponent() != 0;
+ }
+
+ bool doKeyDown (const WPARAM key)
+ {
+ updateKeyModifiers();
+ bool used = false;
+
+ switch (key)
+ {
+ case VK_SHIFT:
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ case VK_CONTROL:
+ case VK_LCONTROL:
+ case VK_RCONTROL:
+ case VK_MENU:
+ case VK_LMENU:
+ case VK_RMENU:
+ case VK_LWIN:
+ case VK_RWIN:
+ case VK_CAPITAL:
+ case VK_NUMLOCK:
+ case VK_SCROLL:
+ case VK_APPS:
+ sendModifierKeyChangeIfNeeded();
+ break;
+
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_UP:
+ case VK_DOWN:
+ case VK_PRIOR:
+ case VK_NEXT:
+ case VK_HOME:
+ case VK_END:
+ case VK_DELETE:
+ case VK_INSERT:
+ case VK_F1:
+ case VK_F2:
+ case VK_F3:
+ case VK_F4:
+ case VK_F5:
+ case VK_F6:
+ case VK_F7:
+ case VK_F8:
+ case VK_F9:
+ case VK_F10:
+ case VK_F11:
+ case VK_F12:
+ case VK_F13:
+ case VK_F14:
+ case VK_F15:
+ case VK_F16:
+ used = handleKeyUpOrDown (true);
+ used = handleKeyPress (extendedKeyModifier | (int) key, 0) || used;
+ break;
+
+ case VK_ADD:
+ case VK_SUBTRACT:
+ case VK_MULTIPLY:
+ case VK_DIVIDE:
+ case VK_SEPARATOR:
+ case VK_DECIMAL:
+ used = handleKeyUpOrDown (true);
+ break;
+
+ default:
+ used = handleKeyUpOrDown (true);
+
+ {
+ MSG msg;
+
+ if (! PeekMessage (&msg, hwnd, WM_CHAR, WM_DEADCHAR, PM_NOREMOVE))
+ {
+ // if there isn't a WM_CHAR or WM_DEADCHAR message pending, we need to
+ // manually generate the key-press event that matches this key-down.
+
+ const UINT keyChar = MapVirtualKey (key, 2);
+ used = handleKeyPress ((int) LOWORD (keyChar), 0) || used;
+ }
+ }
+
+ break;
+ }
+
+ if (Component::getCurrentlyModalComponent() != 0)
+ used = true;
+
+ return used;
+ }
+
+ bool doKeyChar (int key, const LPARAM flags)
+ {
+ updateKeyModifiers();
+
+ juce_wchar textChar = (juce_wchar) key;
+
+ const int virtualScanCode = (flags >> 16) & 0xff;
+
+ if (key >= '0' && key <= '9')
+ {
+ switch (virtualScanCode) // check for a numeric keypad scan-code
+ {
+ case 0x52:
+ case 0x4f:
+ case 0x50:
+ case 0x51:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ key = (key - '0') + KeyPress::numberPad0;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // convert the scan code to an unmodified character code..
+ const UINT virtualKey = MapVirtualKey (virtualScanCode, 1);
+ UINT keyChar = MapVirtualKey (virtualKey, 2);
+
+ keyChar = LOWORD (keyChar);
+
+ if (keyChar != 0)
+ key = (int) keyChar;
+
+ // avoid sending junk text characters for some control-key combinations
+ if (textChar < ' ' && currentModifiers.testFlags (ModifierKeys::ctrlModifier | ModifierKeys::altModifier))
+ textChar = 0;
+ }
+
+ return handleKeyPress (key, textChar);
+ }
+
+ void forwardMessageToParent (UINT message, WPARAM wParam, LPARAM lParam) const
+ {
+ HWND parentH = GetParent (hwnd);
+ if (parentH != 0)
+ PostMessage (parentH, message, wParam, lParam);
+ }
+
+ bool doAppCommand (const LPARAM lParam)
+ {
+ int key = 0;
+
+ switch (GET_APPCOMMAND_LPARAM (lParam))
+ {
+ case APPCOMMAND_MEDIA_PLAY_PAUSE: key = KeyPress::playKey; break;
+ case APPCOMMAND_MEDIA_STOP: key = KeyPress::stopKey; break;
+ case APPCOMMAND_MEDIA_NEXTTRACK: key = KeyPress::fastForwardKey; break;
+ case APPCOMMAND_MEDIA_PREVIOUSTRACK: key = KeyPress::rewindKey; break;
+ default: break;
+ }
+
+ if (key != 0)
+ {
+ updateKeyModifiers();
+
+ if (hwnd == GetActiveWindow())
+ {
+ handleKeyPress (key, 0);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool isConstrainedNativeWindow() const
+ {
+ return constrainer != 0
+ && (styleFlags & (windowHasTitleBar | windowIsResizable)) == (windowHasTitleBar | windowIsResizable);
+ }
+
+ LRESULT handleSizeConstraining (RECT* const r, const WPARAM wParam)
+ {
+ if (isConstrainedNativeWindow())
+ {
+ Rectangle pos (r->left, r->top, r->right - r->left, r->bottom - r->top);
+
+ constrainer->checkBounds (pos, windowBorder.addedTo (component->getBounds()),
+ Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),
+ wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT,
+ wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT,
+ wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT,
+ wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT);
+ r->left = pos.getX();
+ r->top = pos.getY();
+ r->right = pos.getRight();
+ r->bottom = pos.getBottom();
+ }
+
+ return TRUE;
+ }
+
+ LRESULT handlePositionChanging (WINDOWPOS* const wp)
+ {
+ if (isConstrainedNativeWindow())
+ {
+ if ((wp->flags & (SWP_NOMOVE | SWP_NOSIZE)) != (SWP_NOMOVE | SWP_NOSIZE)
+ && ! Component::isMouseButtonDownAnywhere())
+ {
+ Rectangle pos (wp->x, wp->y, wp->cx, wp->cy);
+ const Rectangle current (windowBorder.addedTo (component->getBounds()));
+
+ constrainer->checkBounds (pos, current,
+ Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),
+ pos.getY() != current.getY() && pos.getBottom() == current.getBottom(),
+ pos.getX() != current.getX() && pos.getRight() == current.getRight(),
+ pos.getY() == current.getY() && pos.getBottom() != current.getBottom(),
+ pos.getX() == current.getX() && pos.getRight() != current.getRight());
+ wp->x = pos.getX();
+ wp->y = pos.getY();
+ wp->cx = pos.getWidth();
+ wp->cy = pos.getHeight();
+ }
+ }
+
+ return 0;
+ }
+
+ void handleAppActivation (const WPARAM wParam)
+ {
+ modifiersAtLastCallback = -1;
+ updateKeyModifiers();
+
+ if (isMinimised())
+ {
+ component->repaint();
+ handleMovedOrResized();
+
+ if (! ComponentPeer::isValidPeer (this))
+ return;
+ }
+
+ Component* underMouse = component->getComponentAt (component->getMouseXYRelative());
+
+ if (underMouse == 0)
+ underMouse = component;
+
+ if (underMouse->isCurrentlyBlockedByAnotherModalComponent())
+ {
+ if (LOWORD (wParam) == WA_CLICKACTIVE)
+ Component::getCurrentlyModalComponent()->inputAttemptWhenModal();
+ else
+ ModalComponentManager::getInstance()->bringModalComponentsToFront();
+ }
+ else
+ {
+ handleBroughtToFront();
+ }
+ }
+
+ void handleLeftClickInNCArea (WPARAM wParam)
+ {
+ if (! sendInputAttemptWhenModalMessage())
+ {
+ switch (wParam)
+ {
+ case HTBOTTOM:
+ case HTBOTTOMLEFT:
+ case HTBOTTOMRIGHT:
+ case HTGROWBOX:
+ case HTLEFT:
+ case HTRIGHT:
+ case HTTOP:
+ case HTTOPLEFT:
+ case HTTOPRIGHT:
+ if (isConstrainedNativeWindow())
+ {
+ constrainerIsResizing = true;
+ constrainer->resizeStart();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ //==============================================================================
+ class JuceDropTarget : public ComBaseClassHelper
+ {
+ public:
+ JuceDropTarget (Win32ComponentPeer* const owner_)
+ : owner (owner_)
+ {
+ }
+
+ HRESULT __stdcall DragEnter (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
+ {
+ updateFileList (pDataObject);
+ owner->handleFileDragMove (files, owner->globalToLocal (Point (mousePos.x, mousePos.y)));
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+ }
+
+ HRESULT __stdcall DragLeave()
+ {
+ owner->handleFileDragExit (files);
+ return S_OK;
+ }
+
+ HRESULT __stdcall DragOver (DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
+ {
+ owner->handleFileDragMove (files, owner->globalToLocal (Point (mousePos.x, mousePos.y)));
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+ }
+
+ HRESULT __stdcall Drop (IDataObject* pDataObject, DWORD /*grfKeyState*/, POINTL mousePos, DWORD* pdwEffect)
+ {
+ updateFileList (pDataObject);
+ owner->handleFileDragDrop (files, owner->globalToLocal (Point (mousePos.x, mousePos.y)));
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+ }
+
+ private:
+ Win32ComponentPeer* const owner;
+ StringArray files;
+
+ void updateFileList (IDataObject* const pDataObject)
+ {
+ files.clear();
+
+ FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
+
+ if (pDataObject->GetData (&format, &medium) == S_OK)
+ {
+ const SIZE_T totalLen = GlobalSize (medium.hGlobal);
+ const LPDROPFILES pDropFiles = (const LPDROPFILES) GlobalLock (medium.hGlobal);
+ unsigned int i = 0;
+
+ if (pDropFiles->fWide)
+ {
+ const WCHAR* const fname = (WCHAR*) addBytesToPointer (pDropFiles, sizeof (DROPFILES));
+
+ for (;;)
+ {
+ unsigned int len = 0;
+ while (i + len < totalLen && fname [i + len] != 0)
+ ++len;
+
+ if (len == 0)
+ break;
+
+ files.add (String (fname + i, len));
+ i += len + 1;
+ }
+ }
+ else
+ {
+ const char* const fname = (const char*) addBytesToPointer (pDropFiles, sizeof (DROPFILES));
+
+ for (;;)
+ {
+ unsigned int len = 0;
+ while (i + len < totalLen && fname [i + len] != 0)
+ ++len;
+
+ if (len == 0)
+ break;
+
+ files.add (String (fname + i, len));
+ i += len + 1;
+ }
+ }
+
+ GlobalUnlock (medium.hGlobal);
+ }
+ }
+
+ JUCE_DECLARE_NON_COPYABLE (JuceDropTarget);
+ };
+
+ void doSettingChange()
+ {
+ Desktop::getInstance().refreshMonitorSizes();
+
+ if (fullScreen && ! isMinimised())
+ {
+ const Rectangle r (component->getParentMonitorArea());
+
+ SetWindowPos (hwnd, 0, r.getX(), r.getY(), r.getWidth(), r.getHeight(),
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSENDCHANGING);
+ }
+ }
+
+ //==============================================================================
+public:
+ static LRESULT CALLBACK windowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
+ {
+ Win32ComponentPeer* const peer = getOwnerOfWindow (h);
+
+ if (peer != 0)
+ {
+ jassert (isValidPeer (peer));
+ return peer->peerWindowProc (h, message, wParam, lParam);
+ }
+
+ return DefWindowProcW (h, message, wParam, lParam);
+ }
+
+private:
+ static void* callFunctionIfNotLocked (MessageCallbackFunction* callback, void* userData)
+ {
+ if (MessageManager::getInstance()->currentThreadHasLockedMessageManager())
+ return callback (userData);
+ else
+ return MessageManager::getInstance()->callFunctionOnMessageThread (callback, userData);
+ }
+
+ static const Point getPointFromLParam (LPARAM lParam) throw()
+ {
+ return Point (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
+ }
+
+ const Point getCurrentMousePos() throw()
+ {
+ RECT wr;
+ GetWindowRect (hwnd, &wr);
+ const DWORD mp = GetMessagePos();
+
+ return Point (GET_X_LPARAM (mp) - wr.left - windowBorder.getLeft(),
+ GET_Y_LPARAM (mp) - wr.top - windowBorder.getTop());
+ }
+
+ LRESULT peerWindowProc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
+ {
+ switch (message)
+ {
+ //==============================================================================
+ case WM_NCHITTEST:
+ if ((styleFlags & windowIgnoresMouseClicks) != 0)
+ return HTTRANSPARENT;
+ else if (! hasTitleBar())
+ return HTCLIENT;
+
+ break;
+
+ //==============================================================================
+ case WM_PAINT:
+ handlePaintMessage();
+ return 0;
+
+ case WM_NCPAINT:
+ if (wParam != 1) // (1 = a repaint of the entire NC region)
+ handlePaintMessage(); // this must be done, even with native titlebars, or there are rendering artifacts.
+
+ if (hasTitleBar())
+ break; // let the DefWindowProc handle drawing the frame.
+
+ return 0;
+
+ case WM_ERASEBKGND:
+ case WM_NCCALCSIZE:
+ if (hasTitleBar())
+ break;
+
+ return 1;
+
+ //==============================================================================
+ case WM_MOUSEMOVE:
+ doMouseMove (getPointFromLParam (lParam));
+ return 0;
+
+ case WM_MOUSELEAVE:
+ doMouseExit();
+ return 0;
+
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ doMouseDown (getPointFromLParam (lParam), wParam);
+ return 0;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ doMouseUp (getPointFromLParam (lParam), wParam);
+ return 0;
+
+ case WM_CAPTURECHANGED:
+ doCaptureChanged();
+ return 0;
+
+ case WM_NCMOUSEMOVE:
+ if (hasTitleBar())
+ break;
+
+ return 0;
+
+ case 0x020A: /* WM_MOUSEWHEEL */
+ case 0x020E: /* WM_MOUSEHWHEEL */
+ doMouseWheel (getCurrentMousePos(), wParam, message == 0x020A);
+ return 0;
+
+ //==============================================================================
+ case WM_SIZING:
+ return handleSizeConstraining ((RECT*) lParam, wParam);
+
+ case WM_WINDOWPOSCHANGING:
+ return handlePositionChanging ((WINDOWPOS*) lParam);
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ const Point pos (getCurrentMousePos());
+ if (contains (pos, false))
+ doMouseEvent (pos);
+ }
+
+ handleMovedOrResized();
+
+ if (dontRepaint)
+ break; // needed for non-accelerated openGL windows to draw themselves correctly..
+
+ return 0;
+
+ //==============================================================================
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ if (doKeyDown (wParam))
+ return 0;
+ else
+ forwardMessageToParent (message, wParam, lParam);
+
+ break;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ if (doKeyUp (wParam))
+ return 0;
+ else
+ forwardMessageToParent (message, wParam, lParam);
+
+ break;
+
+ case WM_CHAR:
+ if (doKeyChar ((int) wParam, lParam))
+ return 0;
+ else
+ forwardMessageToParent (message, wParam, lParam);
+
+ break;
+
+ case WM_APPCOMMAND:
+ if (doAppCommand (lParam))
+ return TRUE;
+
+ break;
+
+ //==============================================================================
+ case WM_SETFOCUS:
+ updateKeyModifiers();
+ handleFocusGain();
+ break;
+
+ case WM_KILLFOCUS:
+ if (hasCreatedCaret)
+ {
+ hasCreatedCaret = false;
+ DestroyCaret();
+ }
+
+ handleFocusLoss();
+ break;
+
+ case WM_ACTIVATEAPP:
+ // Windows does weird things to process priority when you swap apps,
+ // so this forces an update when the app is brought to the front
+ if (wParam != FALSE)
+ juce_repeatLastProcessPriority();
+ else
+ Desktop::getInstance().setKioskModeComponent (0); // turn kiosk mode off if we lose focus
+
+ juce_CheckCurrentlyFocusedTopLevelWindow();
+ modifiersAtLastCallback = -1;
+ return 0;
+
+ case WM_ACTIVATE:
+ if (LOWORD (wParam) == WA_ACTIVE || LOWORD (wParam) == WA_CLICKACTIVE)
+ {
+ handleAppActivation (wParam);
+ return 0;
+ }
+
+ break;
+
+ case WM_NCACTIVATE:
+ // while a temporary window is being shown, prevent Windows from deactivating the
+ // title bars of our main windows.
+ if (wParam == 0 && ! shouldDeactivateTitleBar)
+ wParam = TRUE; // change this and let it get passed to the DefWindowProc.
+
+ break;
+
+ case WM_MOUSEACTIVATE:
+ if (! component->getMouseClickGrabsKeyboardFocus())
+ return MA_NOACTIVATE;
+
+ break;
+
+ case WM_SHOWWINDOW:
+ if (wParam != 0)
+ handleBroughtToFront();
+
+ break;
+
+ case WM_CLOSE:
+ if (! component->isCurrentlyBlockedByAnotherModalComponent())
+ handleUserClosingWindow();
+
+ return 0;
+
+ case WM_QUERYENDSESSION:
+ if (JUCEApplication::getInstance() != 0)
+ {
+ JUCEApplication::getInstance()->systemRequestedQuit();
+ return MessageManager::getInstance()->hasStopMessageBeenSent();
+ }
+ return TRUE;
+
+ case WM_TRAYNOTIFY:
+ handleTaskBarEvent (lParam);
+ break;
+
+ case WM_SYNCPAINT:
+ return 0;
+
+ case WM_DISPLAYCHANGE:
+ InvalidateRect (h, 0, 0);
+ // intentional fall-through...
+ case WM_SETTINGCHANGE: // note the fall-through in the previous case!
+ doSettingChange();
+ break;
+
+ case WM_INITMENU:
+ if (! hasTitleBar())
+ {
+ if (isFullScreen())
+ {
+ EnableMenuItem ((HMENU) wParam, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem ((HMENU) wParam, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
+ }
+ else if (! isMinimised())
+ {
+ EnableMenuItem ((HMENU) wParam, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
+ }
+ }
+ break;
+
+ case WM_SYSCOMMAND:
+ switch (wParam & 0xfff0)
+ {
+ case SC_CLOSE:
+ if (sendInputAttemptWhenModalMessage())
+ return 0;
+
+ if (hasTitleBar())
+ {
+ PostMessage (h, WM_CLOSE, 0, 0);
+ return 0;
+ }
+ break;
+
+ case SC_KEYMENU:
+ // (NB mustn't call sendInputAttemptWhenModalMessage() here because of very obscure
+ // situations that can arise if a modal loop is started from an alt-key keypress).
+ if (hasTitleBar() && h == GetCapture())
+ ReleaseCapture();
+
+ break;
+
+ case SC_MAXIMIZE:
+ if (! sendInputAttemptWhenModalMessage())
+ setFullScreen (true);
+
+ return 0;
+
+ case SC_MINIMIZE:
+ if (sendInputAttemptWhenModalMessage())
+ return 0;
+
+ if (! hasTitleBar())
+ {
+ setMinimised (true);
+ return 0;
+ }
+ break;
+
+ case SC_RESTORE:
+ if (sendInputAttemptWhenModalMessage())
+ return 0;
+
+ if (hasTitleBar())
+ {
+ if (isFullScreen())
+ {
+ setFullScreen (false);
+ return 0;
+ }
+ }
+ else
+ {
+ if (isMinimised())
+ setMinimised (false);
+ else if (isFullScreen())
+ setFullScreen (false);
+
+ return 0;
+ }
+ break;
+ }
+
+ break;
+
+ case WM_NCLBUTTONDOWN:
+ handleLeftClickInNCArea (wParam);
+ break;
+
+ case WM_NCRBUTTONDOWN:
+ case WM_NCMBUTTONDOWN:
+ sendInputAttemptWhenModalMessage();
+ break;
+
+ case WM_IME_SETCONTEXT:
+ imeHandler.handleSetContext (h, wParam == TRUE);
+ lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
+ break;
+
+ case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0;
+ case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); break;
+ case WM_IME_COMPOSITION: imeHandler.handleComposition (*this, h, lParam); return 0;
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTALLKEYS;
+
+ default:
+ if (taskBarIcon != 0)
+ {
+ static const DWORD taskbarCreatedMessage = RegisterWindowMessage (TEXT("TaskbarCreated"));
+
+ if (message == taskbarCreatedMessage)
+ {
+ taskBarIcon->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+ Shell_NotifyIcon (NIM_ADD, taskBarIcon);
+ }
+ }
+
+ break;
+ }
+
+ return DefWindowProcW (h, message, wParam, lParam);
+ }
+
+ bool sendInputAttemptWhenModalMessage()
+ {
+ if (component->isCurrentlyBlockedByAnotherModalComponent())
+ {
+ Component* const current = Component::getCurrentlyModalComponent();
+
+ if (current != 0)
+ current->inputAttemptWhenModal();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ //==============================================================================
+ class IMEHandler
+ {
+ public:
+ IMEHandler()
+ {
+ reset();
+ }
+
+ void handleSetContext (HWND hWnd, const bool windowIsActive)
+ {
+ if (compositionInProgress && ! windowIsActive)
+ {
+ compositionInProgress = false;
+
+ HIMC hImc = ImmGetContext (hWnd);
+ if (hImc != 0)
+ {
+ ImmNotifyIME (hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
+ ImmReleaseContext (hWnd, hImc);
+ }
+ }
+ }
+
+ void handleStartComposition (ComponentPeer& owner)
+ {
+ reset();
+ TextInputTarget* const target = owner.findCurrentTextInputTarget();
+
+ if (target != 0)
+ target->insertTextAtCaret (String::empty);
+ }
+
+ void handleEndComposition (ComponentPeer& owner, HWND hWnd)
+ {
+ if (compositionInProgress)
+ {
+ // If this occurs, the user has cancelled the composition, so clear their changes..
+ TextInputTarget* const target = owner.findCurrentTextInputTarget();
+
+ if (target != 0)
+ {
+ target->setHighlightedRegion (compositionRange);
+ target->insertTextAtCaret (String::empty);
+ compositionRange.setLength (0);
+
+ target->setHighlightedRegion (Range::emptyRange (compositionRange.getEnd()));
+ target->setTemporaryUnderlining (Array >());
+ }
+
+ HIMC hImc = ImmGetContext (hWnd);
+
+ if (hImc != 0)
+ {
+ ImmNotifyIME (hImc, NI_CLOSECANDIDATE, 0, 0);
+ ImmReleaseContext (hWnd, hImc);
+ }
+ }
+
+ reset();
+ }
+
+ void handleComposition (ComponentPeer& owner, HWND hWnd, const LPARAM lParam)
+ {
+ TextInputTarget* const target = owner.findCurrentTextInputTarget();
+ HIMC hImc = ImmGetContext (hWnd);
+
+ if (target == 0 || hImc == 0)
+ return;
+
+ if (compositionRange.getStart() < 0)
+ compositionRange = Range::emptyRange (target->getHighlightedRegion().getStart());
+
+ if ((lParam & GCS_RESULTSTR) != 0) // (composition has finished)
+ {
+ replaceCurrentSelection (target, getCompositionString (hImc, GCS_RESULTSTR),
+ Range::emptyRange (compositionRange.getEnd()));
+
+ target->setTemporaryUnderlining (Array >());
+
+ compositionInProgress = false;
+ }
+ else if ((lParam & GCS_COMPSTR) != 0) // (composition is still in-progress)
+ {
+ const String newContent (getCompositionString (hImc, GCS_COMPSTR));
+ const Range selection (getCompositionSelection (hImc, lParam));
+
+ replaceCurrentSelection (target, newContent, selection);
+
+ target->setTemporaryUnderlining (getCompositionUnderlines (hImc, lParam));
+ compositionInProgress = true;
+ }
+
+ moveCandidateWindowToLeftAlignWithSelection (hImc, owner, target);
+ ImmReleaseContext (hWnd, hImc);
+ }
+
+ private:
+ //==============================================================================
+ Range compositionRange; // The range being modified in the TextInputTarget
+ bool compositionInProgress;
+
+ //==============================================================================
+ void reset()
+ {
+ compositionRange = Range::emptyRange (-1);
+ compositionInProgress = false;
+ }
+
+ const String getCompositionString (HIMC hImc, const DWORD type) const
+ {
+ jassert (hImc != 0);
+
+ const int stringSizeBytes = ImmGetCompositionString (hImc, type, 0, 0);
+
+ if (stringSizeBytes > 0)
+ {
+ HeapBlock buffer;
+ buffer.calloc (stringSizeBytes / sizeof (TCHAR) + 1);
+ ImmGetCompositionString (hImc, type, buffer, stringSizeBytes);
+ return String (buffer);
+ }
+
+ return String::empty;
+ }
+
+ int getCompositionCaretPos (HIMC hImc, LPARAM lParam, const String& currentIMEString) const
+ {
+ jassert (hImc != 0);
+
+ if ((lParam & CS_NOMOVECARET) != 0)
+ return compositionRange.getStart();
+
+ if ((lParam & GCS_CURSORPOS) != 0)
+ {
+ const int localCaretPos = ImmGetCompositionString (hImc, GCS_CURSORPOS, 0, 0);
+ return compositionRange.getStart() + jmax (0, localCaretPos);
+ }
+
+ return compositionRange.getStart() + currentIMEString.length();
+ }
+
+ // Get selected/highlighted range while doing composition:
+ // returned range is relative to beginning of TextInputTarget, not composition string
+ const Range getCompositionSelection (HIMC hImc, LPARAM lParam) const
+ {
+ jassert (hImc != 0);
+ int selectionStart = 0;
+ int selectionEnd = 0;
+
+ if ((lParam & GCS_COMPATTR) != 0)
+ {
+ // Get size of attributes array:
+ const int attributeSizeBytes = ImmGetCompositionString (hImc, GCS_COMPATTR, 0, 0);
+
+ if (attributeSizeBytes > 0)
+ {
+ // Get attributes (8 bit flag per character):
+ HeapBlock attributes (attributeSizeBytes);
+ ImmGetCompositionString (hImc, GCS_COMPATTR, attributes, attributeSizeBytes);
+
+ selectionStart = 0;
+
+ for (selectionStart = 0; selectionStart < attributeSizeBytes; ++selectionStart)
+ if (attributes[selectionStart] == ATTR_TARGET_CONVERTED || attributes[selectionStart] == ATTR_TARGET_NOTCONVERTED)
+ break;
+
+ for (selectionEnd = selectionStart; selectionEnd < attributeSizeBytes; ++selectionEnd)
+ if (attributes [selectionEnd] != ATTR_TARGET_CONVERTED && attributes[selectionEnd] != ATTR_TARGET_NOTCONVERTED)
+ break;
+ }
+ }
+
+ return Range (selectionStart, selectionEnd) + compositionRange.getStart();
+ }
+
+ void replaceCurrentSelection (TextInputTarget* const target, const String& newContent, const Range& newSelection)
+ {
+ target->setHighlightedRegion (compositionRange);
+ target->insertTextAtCaret (newContent);
+ compositionRange.setLength (newContent.length());
+
+ target->setHighlightedRegion (newSelection);
+ }
+
+ const Array > getCompositionUnderlines (HIMC hImc, LPARAM lParam) const
+ {
+ Array > result;
+
+ if (hImc != 0 && (lParam & GCS_COMPCLAUSE) != 0)
+ {
+ const int clauseDataSizeBytes = ImmGetCompositionString (hImc, GCS_COMPCLAUSE, 0, 0);
+
+ if (clauseDataSizeBytes > 0)
+ {
+ const int numItems = clauseDataSizeBytes / sizeof (uint32);
+ HeapBlock clauseData (numItems);
+
+ if (ImmGetCompositionString (hImc, GCS_COMPCLAUSE, clauseData, clauseDataSizeBytes) > 0)
+ for (int i = 0; i < numItems - 1; ++i)
+ result.add (Range (clauseData [i], clauseData [i + 1]) + compositionRange.getStart());
+ }
+ }
+
+ return result;
+ }
+
+ void moveCandidateWindowToLeftAlignWithSelection (HIMC hImc, ComponentPeer& peer, TextInputTarget* target) const
+ {
+ Component* const targetComp = dynamic_cast (target);
+
+ if (targetComp != 0)
+ {
+ const Rectangle area (peer.getComponent()
+ ->getLocalArea (targetComp, target->getCaretRectangle()));
+
+ CANDIDATEFORM pos = { 0, CFS_CANDIDATEPOS, { area.getX(), area.getBottom() }, { 0, 0, 0, 0 } };
+ ImmSetCandidateWindow (hImc, &pos);
+ }
+ }
+
+ JUCE_DECLARE_NON_COPYABLE (IMEHandler);
+ };
+
+ IMEHandler imeHandler;
+
+ //==============================================================================
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32ComponentPeer);
+};
+
+ModifierKeys Win32ComponentPeer::currentModifiers;
+ModifierKeys Win32ComponentPeer::modifiersAtLastCallback;
+
+ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
+{
+ return new Win32ComponentPeer (this, styleFlags, (HWND) nativeWindowToAttachTo);
+}
+
+juce_ImplementSingleton_SingleThreaded (Win32ComponentPeer::WindowClassHolder);
+
+
+//==============================================================================
+void ModifierKeys::updateCurrentModifiers() throw()
+{
+ currentModifiers = Win32ComponentPeer::currentModifiers;
+}
+
+const ModifierKeys ModifierKeys::getCurrentModifiersRealtime() throw()
+{
+ Win32ComponentPeer::updateKeyModifiers();
+
+ int mouseMods = 0;
+ if (Win32ComponentPeer::isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier;
+ if (Win32ComponentPeer::isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier;
+ if (Win32ComponentPeer::isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier;
+
+ Win32ComponentPeer::currentModifiers
+ = Win32ComponentPeer::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
+
+ return Win32ComponentPeer::currentModifiers;
+}
+
+//==============================================================================
+bool KeyPress::isKeyCurrentlyDown (const int keyCode)
+{
+ SHORT k = (SHORT) keyCode;
+
+ if ((keyCode & extendedKeyModifier) == 0
+ && (k >= (SHORT) 'a' && k <= (SHORT) 'z'))
+ k += (SHORT) 'A' - (SHORT) 'a';
+
+ const SHORT translatedValues[] = { (SHORT) ',', VK_OEM_COMMA,
+ (SHORT) '+', VK_OEM_PLUS,
+ (SHORT) '-', VK_OEM_MINUS,
+ (SHORT) '.', VK_OEM_PERIOD,
+ (SHORT) ';', VK_OEM_1,
+ (SHORT) ':', VK_OEM_1,
+ (SHORT) '/', VK_OEM_2,
+ (SHORT) '?', VK_OEM_2,
+ (SHORT) '[', VK_OEM_4,
+ (SHORT) ']', VK_OEM_6 };
+
+ for (int i = 0; i < numElementsInArray (translatedValues); i += 2)
+ if (k == translatedValues [i])
+ k = translatedValues [i + 1];
+
+ return Win32ComponentPeer::isKeyDown (k);
+}
+
+//==============================================================================
+void SystemTrayIconComponent::setIconImage (const Image& newImage)
+{
+ Win32ComponentPeer* const wp = dynamic_cast (getPeer());
+
+ if (wp != 0)
+ wp->setTaskBarIcon (newImage);
+}
+
+void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
+{
+ Win32ComponentPeer* const wp = dynamic_cast (getPeer());
+
+ if (wp != 0)
+ wp->setTaskBarIconToolTip (tooltip);
+}
+
+//==============================================================================
+void juce_setWindowStyleBit (HWND h, const int styleType, const int feature, const bool bitIsSet) throw()
+{
+ DWORD val = GetWindowLong (h, styleType);
+
+ if (bitIsSet)
+ val |= feature;
+ else
+ val &= ~feature;
+
+ SetWindowLongPtr (h, styleType, val);
+ SetWindowPos (h, 0, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
+ | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSENDCHANGING);
+}
+
+
+//==============================================================================
+bool Process::isForegroundProcess()
+{
+ HWND fg = GetForegroundWindow();
+
+ if (fg == 0)
+ return true;
+
+ // when running as a plugin in IE8, the browser UI runs in a different process to the plugin, so
+ // process ID isn't a reliable way to check if the foreground window belongs to us - instead, we
+ // have to see if any of our windows are children of the foreground window
+ fg = GetAncestor (fg, GA_ROOT);
+
+ for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
+ {
+ Win32ComponentPeer* const wp = dynamic_cast (ComponentPeer::getPeer (i));
+
+ if (wp != 0 && wp->isInside (fg))
+ return true;
+ }
+
+ return false;
+}
+
+//==============================================================================
+class Win32MessageBox : public AsyncUpdater
+{
+public:
+ Win32MessageBox (AlertWindow::AlertIconType iconType,
+ const String& title_, const String& message_,
+ Component* associatedComponent,
+ UINT extraFlags,
+ ModalComponentManager::Callback* callback_,
+ const bool runAsync)
+ : flags (extraFlags | getMessageBoxFlags (iconType)),
+ owner (getWindowForMessageBox (associatedComponent)),
+ title (title_), message (message_), callback (callback_)
+ {
+ if (runAsync)
+ triggerAsyncUpdate();
+ }
+
+ int getResult() const
+ {
+ const int r = MessageBox (owner, message.toWideCharPointer(), title.toWideCharPointer(), flags);
+ return (r == IDYES || r == IDOK) ? 1 : (r == IDNO ? 2 : 0);
+ }
+
+ void handleAsyncUpdate()
+ {
+ const int result = getResult();
+
+ if (callback != 0)
+ callback->modalStateFinished (result);
+
+ delete this;
+ }
+
+private:
+ UINT flags;
+ HWND owner;
+ String title, message;
+ ModalComponentManager::Callback* callback;
+
+ static UINT getMessageBoxFlags (AlertWindow::AlertIconType iconType) throw()
+ {
+ UINT flags = MB_TASKMODAL | MB_SETFOREGROUND;
+
+ switch (iconType)
+ {
+ case AlertWindow::QuestionIcon: flags |= MB_ICONQUESTION; break;
+ case AlertWindow::WarningIcon: flags |= MB_ICONWARNING; break;
+ case AlertWindow::InfoIcon: flags |= MB_ICONINFORMATION; break;
+ default: break;
+ }
+
+ return flags;
+ }
+
+ static HWND getWindowForMessageBox (Component* associatedComponent)
+ {
+ return associatedComponent != 0 ? (HWND) associatedComponent->getWindowHandle() : 0;
+ }
+};
+
+void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ Win32MessageBox box (iconType, title, message, associatedComponent, MB_OK, 0, false);
+ (void) box.getResult();
+}
+
+void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent)
+{
+ new Win32MessageBox (iconType, title, message, associatedComponent, MB_OK, 0, true);
+}
+
+bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new Win32MessageBox (iconType, title, message, associatedComponent,
+ MB_OKCANCEL, callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult() != 0;
+
+ mb.release();
+ return 0;
+}
+
+int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
+ const String& title, const String& message,
+ Component* associatedComponent,
+ ModalComponentManager::Callback* callback)
+{
+ ScopedPointer mb (new Win32MessageBox (iconType, title, message, associatedComponent,
+ MB_YESNOCANCEL, callback, callback != 0));
+ if (callback == 0)
+ return mb->getResult();
+
+ mb.release();
+ return 0;
+}
+
+//==============================================================================
+void Desktop::createMouseInputSources()
+{
+ mouseSources.add (new MouseInputSource (0, true));
+}
+
+const Point MouseInputSource::getCurrentMousePosition()
+{
+ POINT mousePos;
+ GetCursorPos (&mousePos);
+ return Point (mousePos.x, mousePos.y);
+}
+
+void Desktop::setMousePosition (const Point& newPosition)
+{
+ SetCursorPos (newPosition.getX(), newPosition.getY());
+}
+
+//==============================================================================
+Image::SharedImage* Image::SharedImage::createNativeImage (PixelFormat format, int width, int height, bool clearImage)
+{
+ return createSoftwareImage (format, width, height, clearImage);
+}
+
+//==============================================================================
+class ScreenSaverDefeater : public Timer,
+ public DeletedAtShutdown
+{
+public:
+ ScreenSaverDefeater()
+ {
+ startTimer (10000);
+ timerCallback();
+ }
+
+ void timerCallback()
+ {
+ if (Process::isForegroundProcess())
+ {
+ // simulate a shift key getting pressed..
+ INPUT input[2];
+ input[0].type = INPUT_KEYBOARD;
+ input[0].ki.wVk = VK_SHIFT;
+ input[0].ki.dwFlags = 0;
+ input[0].ki.dwExtraInfo = 0;
+
+ input[1].type = INPUT_KEYBOARD;
+ input[1].ki.wVk = VK_SHIFT;
+ input[1].ki.dwFlags = KEYEVENTF_KEYUP;
+ input[1].ki.dwExtraInfo = 0;
+
+ SendInput (2, input, sizeof (INPUT));
+ }
+ }
+};
+
+static ScreenSaverDefeater* screenSaverDefeater = 0;
+
+void Desktop::setScreenSaverEnabled (const bool isEnabled)
+{
+ if (isEnabled)
+ deleteAndZero (screenSaverDefeater);
+ else if (screenSaverDefeater == 0)
+ screenSaverDefeater = new ScreenSaverDefeater();
+}
+
+bool Desktop::isScreenSaverEnabled()
+{
+ return screenSaverDefeater == 0;
+}
+
+/* (The code below is the "correct" way to disable the screen saver, but it
+ completely fails on winXP when the saver is password-protected...)
+
+static bool juce_screenSaverEnabled = true;
+
+void Desktop::setScreenSaverEnabled (const bool isEnabled) throw()
+{
+ juce_screenSaverEnabled = isEnabled;
+ SetThreadExecutionState (isEnabled ? ES_CONTINUOUS
+ : (ES_DISPLAY_REQUIRED | ES_CONTINUOUS));
+}
+
+bool Desktop::isScreenSaverEnabled() throw()
+{
+ return juce_screenSaverEnabled;
+}
+*/
+
+//==============================================================================
+void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool /*allowMenusAndBars*/)
+{
+ if (enableOrDisable)
+ kioskModeComponent->setBounds (Desktop::getInstance().getMainMonitorArea (false));
+}
+
+//==============================================================================
+static BOOL CALLBACK enumMonitorsProc (HMONITOR, HDC, LPRECT r, LPARAM userInfo)
+{
+ Array >* const monitorCoords = (Array >*) userInfo;
+ monitorCoords->add (Rectangle (r->left, r->top, r->right - r->left, r->bottom - r->top));
+ return TRUE;
+}
+
+void Desktop::getCurrentMonitorPositions (Array >& monitorCoords, const bool clipToWorkArea)
+{
+ EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitorCoords);
+
+ // make sure the first in the list is the main monitor
+ for (int i = 1; i < monitorCoords.size(); ++i)
+ if (monitorCoords[i].getX() == 0 && monitorCoords[i].getY() == 0)
+ monitorCoords.swap (i, 0);
+
+ if (monitorCoords.size() == 0)
+ {
+ RECT r;
+ GetWindowRect (GetDesktopWindow(), &r);
+
+ monitorCoords.add (Rectangle (r.left, r.top, r.right - r.left, r.bottom - r.top));
+ }
+
+ if (clipToWorkArea)
+ {
+ // clip the main monitor to the active non-taskbar area
+ RECT r;
+ SystemParametersInfo (SPI_GETWORKAREA, 0, &r, 0);
+
+ Rectangle& screen = monitorCoords.getReference (0);
+
+ screen.setPosition (jmax (screen.getX(), (int) r.left),
+ jmax (screen.getY(), (int) r.top));
+
+ screen.setSize (jmin (screen.getRight(), (int) r.right) - screen.getX(),
+ jmin (screen.getBottom(), (int) r.bottom) - screen.getY());
+ }
+}
+
+//==============================================================================
+const Image juce_createIconForFile (const File& file)
+{
+ Image image;
+ WORD iconNum = 0;
+
+ HICON icon = ExtractAssociatedIcon ((HINSTANCE) PlatformUtilities::getCurrentModuleInstanceHandle(),
+ const_cast (file.getFullPathName().toWideCharPointer()), &iconNum);
+
+ if (icon != 0)
+ {
+ image = IconConverters::createImageFromHICON (icon);
+ DestroyIcon (icon);
+ }
+
+ return image;
+}
+
+//==============================================================================
+void* MouseCursor::createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY)
+{
+ const int maxW = GetSystemMetrics (SM_CXCURSOR);
+ const int maxH = GetSystemMetrics (SM_CYCURSOR);
+
+ Image im (image);
+
+ if (im.getWidth() > maxW || im.getHeight() > maxH)
+ {
+ im = im.rescaled (maxW, maxH);
+
+ hotspotX = (hotspotX * maxW) / image.getWidth();
+ hotspotY = (hotspotY * maxH) / image.getHeight();
+ }
+
+ return IconConverters::createHICONFromImage (im, FALSE, hotspotX, hotspotY);
+}
+
+void MouseCursor::deleteMouseCursor (void* const cursorHandle, const bool isStandard)
+{
+ if (cursorHandle != 0 && ! isStandard)
+ DestroyCursor ((HCURSOR) cursorHandle);
+}
+
+enum
+{
+ hiddenMouseCursorHandle = 32500 // (arbitrary non-zero value to mark this type of cursor)
+};
+
+void* MouseCursor::createStandardMouseCursor (const MouseCursor::StandardCursorType type)
+{
+ LPCTSTR cursorName = IDC_ARROW;
+
+ switch (type)
+ {
+ case NormalCursor: break;
+ case NoCursor: return (void*) hiddenMouseCursorHandle;
+ case WaitCursor: cursorName = IDC_WAIT; break;
+ case IBeamCursor: cursorName = IDC_IBEAM; break;
+ case PointingHandCursor: cursorName = MAKEINTRESOURCE(32649); break;
+ case CrosshairCursor: cursorName = IDC_CROSS; break;
+ case CopyingCursor: break; // can't seem to find one of these in the win32 list..
+
+ case LeftRightResizeCursor:
+ case LeftEdgeResizeCursor:
+ case RightEdgeResizeCursor: cursorName = IDC_SIZEWE; break;
+
+ case UpDownResizeCursor:
+ case TopEdgeResizeCursor:
+ case BottomEdgeResizeCursor: cursorName = IDC_SIZENS; break;
+
+ case TopLeftCornerResizeCursor:
+ case BottomRightCornerResizeCursor: cursorName = IDC_SIZENWSE; break;
+
+ case TopRightCornerResizeCursor:
+ case BottomLeftCornerResizeCursor: cursorName = IDC_SIZENESW; break;
+
+ case UpDownLeftRightResizeCursor: cursorName = IDC_SIZEALL; break;
+
+ case DraggingHandCursor:
+ {
+ static void* dragHandCursor = 0;
+
+ if (dragHandCursor == 0)
+ {
+ static const unsigned char dragHandData[] =
+ { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,
+ 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138,
+ 98,22,203,114,34,236,37,52,77,217,247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59 };
+
+ dragHandCursor = createMouseCursorFromImage (ImageFileFormat::loadFrom (dragHandData, sizeof (dragHandData)), 8, 7);
+ }
+
+ return dragHandCursor;
+ }
+
+ default:
+ jassertfalse; break;
+ }
+
+ HCURSOR cursorH = LoadCursor (0, cursorName);
+
+ if (cursorH == 0)
+ cursorH = LoadCursor (0, IDC_ARROW);
+
+ return cursorH;
+}
+
+//==============================================================================
+void MouseCursor::showInWindow (ComponentPeer*) const
+{
+ HCURSOR c = (HCURSOR) getHandle();
+
+ if (c == 0)
+ c = LoadCursor (0, IDC_ARROW);
+ else if (c == (HCURSOR) hiddenMouseCursorHandle)
+ c = 0;
+
+ SetCursor (c);
+}
+
+void MouseCursor::showInAllWindows() const
+{
+ showInWindow (0);
+}
+
+//==============================================================================
+//==============================================================================
+class JuceDropSource : public ComBaseClassHelper
+{
+public:
+ JuceDropSource() {}
+
+ HRESULT __stdcall QueryContinueDrag (BOOL escapePressed, DWORD keys)
+ {
+ if (escapePressed)
+ return DRAGDROP_S_CANCEL;
+
+ if ((keys & (MK_LBUTTON | MK_RBUTTON)) == 0)
+ return DRAGDROP_S_DROP;
+
+ return S_OK;
+ }
+
+ HRESULT __stdcall GiveFeedback (DWORD)
+ {
+ return DRAGDROP_S_USEDEFAULTCURSORS;
+ }
+};
+
+
+class JuceEnumFormatEtc : public ComBaseClassHelper
+{
+public:
+ JuceEnumFormatEtc (const FORMATETC* const format_)
+ : format (format_),
+ index (0)
+ {
+ }
+
+ HRESULT __stdcall Clone (IEnumFORMATETC** result)
+ {
+ if (result == 0)
+ return E_POINTER;
+
+ JuceEnumFormatEtc* const newOne = new JuceEnumFormatEtc (format);
+ newOne->index = index;
+
+ *result = newOne;
+ return S_OK;
+ }
+
+ HRESULT __stdcall Next (ULONG celt, LPFORMATETC lpFormatEtc, ULONG* pceltFetched)
+ {
+ if (pceltFetched != 0)
+ *pceltFetched = 0;
+ else if (celt != 1)
+ return S_FALSE;
+
+ if (index == 0 && celt > 0 && lpFormatEtc != 0)
+ {
+ copyFormatEtc (lpFormatEtc [0], *format);
+ ++index;
+
+ if (pceltFetched != 0)
+ *pceltFetched = 1;
+
+ return S_OK;
+ }
+
+ return S_FALSE;
+ }
+
+ HRESULT __stdcall Skip (ULONG celt)
+ {
+ if (index + (int) celt >= 1)
+ return S_FALSE;
+
+ index += celt;
+ return S_OK;
+ }
+
+ HRESULT __stdcall Reset()
+ {
+ index = 0;
+ return S_OK;
+ }
+
+private:
+ const FORMATETC* const format;
+ int index;
+
+ static void copyFormatEtc (FORMATETC& dest, const FORMATETC& source)
+ {
+ dest = source;
+
+ if (source.ptd != 0)
+ {
+ dest.ptd = (DVTARGETDEVICE*) CoTaskMemAlloc (sizeof (DVTARGETDEVICE));
+ *(dest.ptd) = *(source.ptd);
+ }
+ }
+
+ JUCE_DECLARE_NON_COPYABLE (JuceEnumFormatEtc);
+};
+
+class JuceDataObject : public ComBaseClassHelper
+{
+public:
+ JuceDataObject (JuceDropSource* const dropSource_,
+ const FORMATETC* const format_,
+ const STGMEDIUM* const medium_)
+ : dropSource (dropSource_),
+ format (format_),
+ medium (medium_)
+ {
+ }
+
+ ~JuceDataObject()
+ {
+ jassert (refCount == 0);
+ }
+
+ HRESULT __stdcall GetData (FORMATETC* pFormatEtc, STGMEDIUM* pMedium)
+ {
+ if ((pFormatEtc->tymed & format->tymed) != 0
+ && pFormatEtc->cfFormat == format->cfFormat
+ && pFormatEtc->dwAspect == format->dwAspect)
+ {
+ pMedium->tymed = format->tymed;
+ pMedium->pUnkForRelease = 0;
+
+ if (format->tymed == TYMED_HGLOBAL)
+ {
+ const SIZE_T len = GlobalSize (medium->hGlobal);
+ void* const src = GlobalLock (medium->hGlobal);
+ void* const dst = GlobalAlloc (GMEM_FIXED, len);
+
+ memcpy (dst, src, len);
+
+ GlobalUnlock (medium->hGlobal);
+
+ pMedium->hGlobal = dst;
+ return S_OK;
+ }
+ }
+
+ return DV_E_FORMATETC;
+ }
+
+ HRESULT __stdcall QueryGetData (FORMATETC* f)
+ {
+ if (f == 0)
+ return E_INVALIDARG;
+
+ if (f->tymed == format->tymed
+ && f->cfFormat == format->cfFormat
+ && f->dwAspect == format->dwAspect)
+ return S_OK;
+
+ return DV_E_FORMATETC;
+ }
+
+ HRESULT __stdcall GetCanonicalFormatEtc (FORMATETC*, FORMATETC* pFormatEtcOut)
+ {
+ pFormatEtcOut->ptd = 0;
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall EnumFormatEtc (DWORD direction, IEnumFORMATETC** result)
+ {
+ if (result == 0)
+ return E_POINTER;
+
+ if (direction == DATADIR_GET)
+ {
+ *result = new JuceEnumFormatEtc (format);
+ return S_OK;
+ }
+
+ *result = 0;
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall GetDataHere (FORMATETC*, STGMEDIUM*) { return DATA_E_FORMATETC; }
+ HRESULT __stdcall SetData (FORMATETC*, STGMEDIUM*, BOOL) { return E_NOTIMPL; }
+ HRESULT __stdcall DAdvise (FORMATETC*, DWORD, IAdviseSink*, DWORD*) { return OLE_E_ADVISENOTSUPPORTED; }
+ HRESULT __stdcall DUnadvise (DWORD) { return E_NOTIMPL; }
+ HRESULT __stdcall EnumDAdvise (IEnumSTATDATA**) { return OLE_E_ADVISENOTSUPPORTED; }
+
+private:
+ JuceDropSource* const dropSource;
+ const FORMATETC* const format;
+ const STGMEDIUM* const medium;
+
+ JUCE_DECLARE_NON_COPYABLE (JuceDataObject);
+};
+
+static HDROP createHDrop (const StringArray& fileNames)
+{
+ int totalBytes = 0;
+ for (int i = fileNames.size(); --i >= 0;)
+ totalBytes += CharPointer_UTF16::getBytesRequiredFor (fileNames[i].getCharPointer()) + sizeof (WCHAR);
+
+ HDROP hDrop = (HDROP) GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof (DROPFILES) + totalBytes + 4);
+
+ if (hDrop != 0)
+ {
+ LPDROPFILES pDropFiles = (LPDROPFILES) GlobalLock (hDrop);
+ pDropFiles->pFiles = sizeof (DROPFILES);
+ pDropFiles->fWide = true;
+
+ WCHAR* fname = reinterpret_cast (addBytesToPointer (pDropFiles, sizeof (DROPFILES)));
+
+ for (int i = 0; i < fileNames.size(); ++i)
+ {
+ const int bytesWritten = fileNames[i].copyToUTF16 (fname, 2048);
+ fname = reinterpret_cast (addBytesToPointer (fname, bytesWritten));
+ }
+
+ *fname = 0;
+
+ GlobalUnlock (hDrop);
+ }
+
+ return hDrop;
+}
+
+static bool performDragDrop (FORMATETC* const format, STGMEDIUM* const medium, const DWORD whatToDo)
+{
+ JuceDropSource* const source = new JuceDropSource();
+ JuceDataObject* const data = new JuceDataObject (source, format, medium);
+
+ DWORD effect;
+ const HRESULT res = DoDragDrop (data, source, whatToDo, &effect);
+
+ data->Release();
+ source->Release();
+
+ return res == DRAGDROP_S_DROP;
+}
+
+bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMove)
+{
+ FORMATETC format = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
+
+ medium.hGlobal = createHDrop (files);
+
+ return performDragDrop (&format, &medium, canMove ? (DROPEFFECT_COPY | DROPEFFECT_MOVE)
+ : DROPEFFECT_COPY);
+}
+
+bool DragAndDropContainer::performExternalDragDropOfText (const String& text)
+{
+ FORMATETC format = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM medium = { TYMED_HGLOBAL, { 0 }, 0 };
+
+ const int numBytes = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
+
+ medium.hGlobal = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, numBytes + 2);
+ WCHAR* const data = static_cast (GlobalLock (medium.hGlobal));
+
+ text.copyToUTF16 (data, numBytes);
+ format.cfFormat = CF_UNICODETEXT;
+
+ GlobalUnlock (medium.hGlobal);
+
+ return performDragDrop (&format, &medium, DROPEFFECT_COPY | DROPEFFECT_MOVE);
+}
+
+#endif
diff --git a/src/text/juce_String.h b/src/text/juce_String.h
index 29b58276a6..3208496c09 100644
--- a/src/text/juce_String.h
+++ b/src/text/juce_String.h
@@ -1323,7 +1323,7 @@ std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostre
}
/** Writes a string to an OutputStream as UTF8. */
-JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text);
+JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& stringToWrite);
#endif // __JUCE_STRING_JUCEHEADER__