/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <private/qmainwindowlayout_p.h>
#include <qtoolbar.h>
#include <private/qtoolbarlayout_p.h>
#include <private/qt_cocoa_helpers_mac_p.h>
#ifndef QT_MAC_USE_COCOA
#include <Carbon/Carbon.h>
#else
#include <private/qcocoatoolbardelegate_mac_p.h>
#endif
QT_BEGIN_NAMESPACE
#ifdef QT_NAMESPACE
// namespace up the stuff
#define SS(x) #x
#define S0(x) SS(x)
#define S "com.trolltech.qt-" S0(QT_NAMESPACE) ".qmainwindow.qtoolbarInHIToolbar"
#define SToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".hitoolbar-qtoolbar"
#define SNSToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".qtoolbarInNSToolbar"
#define MacToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".qmainwindow.mactoolbar"
#ifndef QT_MAC_USE_COCOA
static CFStringRef kQToolBarHIToolbarItemClassID = CFSTR(S);
static CFStringRef kQToolBarHIToolbarIdentifier = CFSTR(SToolbar);
#else
static NSString *kQToolBarNSToolbarIdentifier = @SNSToolbar;
#endif
static CFStringRef kQMainWindowMacToolbarID = CFSTR(MacToolbar);
#undef SS
#undef S0
#undef S
#undef SToolbar
#undef SNSToolbar
#undef MacToolbar
#else
#ifndef QT_MAC_USE_COCOA
static CFStringRef kQToolBarHIToolbarItemClassID = CFSTR("com.trolltech.qt.qmainwindow.qtoolbarInHIToolbar");
static CFStringRef kQToolBarHIToolbarIdentifier = CFSTR("com.trolltech.qt.hitoolbar-qtoolbar");
#else
static NSString *kQToolBarNSToolbarIdentifier = @"com.trolltech.qt.qmainwindow.qtoolbarInNSToolbar";
#endif
static CFStringRef kQMainWindowMacToolbarID = CFSTR("com.trolltech.qt.qmainwindow.mactoolbar");
#endif // QT_NAMESPACE
#ifndef QT_MAC_USE_COCOA
static const int kEventParamQToolBar = 'QTBR';
static const int kEventParamQMainWindowLayout = 'QMWL';
const EventTypeSpec qtoolbarEvents[] =
{
{ kEventClassHIObject, kEventHIObjectConstruct },
{ kEventClassHIObject, kEventHIObjectDestruct },
{ kEventClassHIObject, kEventHIObjectInitialize },
{ kEventClassToolbarItem, kEventToolbarItemCreateCustomView }
};
struct QToolBarInHIToolbarInfo
{
QToolBarInHIToolbarInfo(HIToolbarItemRef item)
: toolbarItem(item), mainWindowLayout(0)
{}
HIToolbarItemRef toolbarItem;
QMainWindowLayout *mainWindowLayout;
};
OSStatus QMainWindowLayout::qtoolbarInHIToolbarHandler(EventHandlerCallRef inCallRef,
EventRef event, void *data)
{
OSStatus result = eventNotHandledErr;
QToolBarInHIToolbarInfo *object = static_cast<QToolBarInHIToolbarInfo *>(data);
switch (GetEventClass(event)) {
case kEventClassHIObject:
switch (GetEventKind(event)) {
case kEventHIObjectConstruct:
{
HIObjectRef toolbarItem;
GetEventParameter(event, kEventParamHIObjectInstance, typeHIObjectRef,
0, sizeof( HIObjectRef ), 0, &toolbarItem);
QToolBarInHIToolbarInfo *item = new QToolBarInHIToolbarInfo(toolbarItem);
SetEventParameter(event, kEventParamHIObjectInstance, typeVoidPtr,
sizeof(void *), &item);
result = noErr;
}
break;
case kEventHIObjectInitialize:
result = CallNextEventHandler(inCallRef, event);
if (result == noErr) {
QToolBar *toolbar = 0;
QMainWindowLayout *layout = 0;
GetEventParameter(event, kEventParamQToolBar, typeVoidPtr,
0, sizeof(void *), 0, &toolbar);
GetEventParameter(event, kEventParamQMainWindowLayout, typeVoidPtr,
0, sizeof(void *), 0, &layout);
object->mainWindowLayout = layout;
object->mainWindowLayout->unifiedToolbarHash.insert(object->toolbarItem, toolbar);
HIToolbarItemChangeAttributes(object->toolbarItem,
kHIToolbarItemLabelDisabled, 0);
}
break;
case kEventHIObjectDestruct:
delete object;
result = noErr;
break;
}
break;
case kEventClassToolbarItem:
switch (GetEventKind(event))
{
case kEventToolbarItemCreateCustomView:
{
QToolBar *toolbar
= object->mainWindowLayout->unifiedToolbarHash.value(object->toolbarItem);
if (toolbar) {
HIViewRef hiview = HIViewRef(toolbar->winId());
SetEventParameter(event, kEventParamControlRef, typeControlRef,
sizeof(HIViewRef), &hiview);
result = noErr;
}
}
break;
}
break;
}
return result;
}
void QMainWindowLayout::qtMacHIToolbarRegisterQToolBarInHIToolborItemClass()
{
static bool registered = false;
if (!registered) {
HIObjectRegisterSubclass( kQToolBarHIToolbarItemClassID,
kHIToolbarItemClassID, 0, QMainWindowLayout::qtoolbarInHIToolbarHandler,
GetEventTypeCount(qtoolbarEvents), qtoolbarEvents, 0, 0 );
registered = true;
}
}
static void GetToolbarAllowedItems(CFMutableArrayRef array)
{
CFArrayAppendValue(array, kQToolBarHIToolbarIdentifier);
}
HIToolbarItemRef QMainWindowLayout::createQToolBarInHIToolbarItem(QToolBar *toolbar,
QMainWindowLayout *layout)
{
QMainWindowLayout::qtMacHIToolbarRegisterQToolBarInHIToolborItemClass();
EventRef event;
HIToolbarItemRef result = 0;
CFStringRef identifier = kQToolBarHIToolbarIdentifier;
UInt32 options = kHIToolbarItemAllowDuplicates;
CreateEvent(0, kEventClassHIObject, kEventHIObjectInitialize,
GetCurrentEventTime(), 0, &event);
SetEventParameter(event, kEventParamToolbarItemIdentifier, typeCFStringRef,
sizeof(CFStringRef), &identifier);
SetEventParameter(event, kEventParamAttributes, typeUInt32, sizeof(UInt32), &options);
SetEventParameter(event, kEventParamQToolBar, typeVoidPtr, sizeof(void *), &toolbar);
SetEventParameter(event, kEventParamQMainWindowLayout, typeVoidPtr, sizeof(void *), &layout);
HIObjectCreate(kQToolBarHIToolbarItemClassID, event,
static_cast<HIObjectRef *>(&result));
ReleaseEvent(event);
return result;
}
HIToolbarItemRef QMainWindowLayout::CreateToolbarItemForIdentifier(CFStringRef identifier,
CFTypeRef data)
{
HIToolbarItemRef item = 0;
if (CFStringCompare(kQToolBarHIToolbarIdentifier, identifier,
kCFCompareBackwards) == kCFCompareEqualTo) {
if (data && CFGetTypeID(data) == CFArrayGetTypeID()) {
CFArrayRef array = static_cast<CFArrayRef>(data);
QToolBar *toolbar = static_cast<QToolBar *>(const_cast<void *>(CFArrayGetValueAtIndex(array, 0)));
QMainWindowLayout *layout = static_cast<QMainWindowLayout *>(const_cast<void *>(CFArrayGetValueAtIndex(array, 1)));
item = createQToolBarInHIToolbarItem(toolbar, layout);
}
}
return item;
}
static const EventTypeSpec kToolbarEvents[] = {
{ kEventClassToolbar, kEventToolbarGetDefaultIdentifiers },
{ kEventClassToolbar, kEventToolbarGetAllowedIdentifiers },
{ kEventClassToolbar, kEventToolbarCreateItemWithIdentifier },
{ kEventClassToolbar, kEventToolbarItemAdded },
{ kEventClassToolbar, kEventToolbarItemRemoved }
};
OSStatus QMainWindowLayout::qtmacToolbarDelegate(EventHandlerCallRef, EventRef event, void *data)
{
QMainWindowLayout *mainWindowLayout = static_cast<QMainWindowLayout *>(data);
OSStatus result = eventNotHandledErr;
CFMutableArrayRef array;
CFStringRef identifier;
switch (GetEventKind(event)) {
case kEventToolbarGetDefaultIdentifiers:
case kEventToolbarGetAllowedIdentifiers:
GetEventParameter(event, kEventParamMutableArray, typeCFMutableArrayRef, 0,
sizeof(CFMutableArrayRef), 0, &array);
GetToolbarAllowedItems(array);
result = noErr;
break;
case kEventToolbarCreateItemWithIdentifier: {
HIToolbarItemRef item;
CFTypeRef data = 0;
OSStatus err = GetEventParameter(event, kEventParamToolbarItemIdentifier, typeCFStringRef,
0, sizeof(CFStringRef), 0, &identifier);
err = GetEventParameter(event, kEventParamToolbarItemConfigData, typeCFTypeRef,
0, sizeof(CFTypeRef), 0, &data);
item = CreateToolbarItemForIdentifier(identifier, data);
if (item) {
result = SetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
sizeof(HIToolbarItemRef), &item );
}
break;
}
case kEventToolbarItemAdded: {
// Double check that our "view" of the toolbar is similar.
HIToolbarItemRef item;
CFIndex index;
if (GetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
0, sizeof(HIToolbarItemRef), 0, &item) == noErr
&& GetEventParameter(event, kEventParamIndex, typeCFIndex, 0,
sizeof(CFIndex), 0, &index) == noErr) {
CFRetain(item); // We will watch this until it's removed from the list (or bust).
mainWindowLayout->toolbarItemsCopy.insert(index, item);
QToolBar *toolbar = mainWindowLayout->unifiedToolbarHash.value(item);
if (toolbar) {
int toolbarIndex = mainWindowLayout->qtoolbarsInUnifiedToolbarList.indexOf(toolbar);
if (index != toolbarIndex) {
// Dang, we must be out of sync, rebuild it from the "toolbarItemsCopy"
mainWindowLayout->qtoolbarsInUnifiedToolbarList.clear();
for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
// This will either append the correct toolbar or an
// null toolbar. This is fine because this list
// is really only kept to make sure that things are but in the right order.
mainWindowLayout->qtoolbarsInUnifiedToolbarList.append(
mainWindowLayout->unifiedToolbarHash.value(mainWindowLayout->
toolbarItemsCopy.at(i)));
}
}
}
}
break;
}
case kEventToolbarItemRemoved: {
HIToolbarItemRef item;
if (GetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
0, sizeof(HIToolbarItemRef), 0, &item) == noErr) {
mainWindowLayout->unifiedToolbarHash.remove(item);
for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
if (mainWindowLayout->toolbarItemsCopy.at(i) == item) {
// I know about it, so release it.
mainWindowLayout->toolbarItemsCopy.removeAt(i);
mainWindowLayout->qtoolbarsInUnifiedToolbarList.removeAt(i);
CFRelease(item);
break;
}
}
}
break;
}
}
return result;
}
#endif // ! QT_MAC_USE_COCOA
#ifndef kWindowUnifiedTitleAndToolbarAttribute
#define kWindowUnifiedTitleAndToolbarAttribute (1 << 7)
#endif
void QMainWindowLayout::updateHIToolBarStatus()
{
bool useMacToolbar = layoutState.mainWindow->unifiedTitleAndToolBarOnMac();
#ifndef QT_MAC_USE_COCOA
if (useMacToolbar) {
ChangeWindowAttributes(qt_mac_window_for(layoutState.mainWindow),
kWindowUnifiedTitleAndToolbarAttribute, 0);
} else {
ChangeWindowAttributes(qt_mac_window_for(layoutState.mainWindow),
0, kWindowUnifiedTitleAndToolbarAttribute);
}
#endif
layoutState.mainWindow->setUpdatesEnabled(false); // reduces a little bit of flicker, not all though
if (!useMacToolbar) {
macWindowToolbarShow(layoutState.mainWindow, false);
// Move everything out of the HIToolbar into the main toolbar.
while (!qtoolbarsInUnifiedToolbarList.isEmpty()) {
// Should shrink the list by one every time.
layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, qtoolbarsInUnifiedToolbarList.first());
}
macWindowToolbarSet(qt_mac_window_for(layoutState.mainWindow), 0);
} else {
QList<QToolBar *> toolbars = layoutState.mainWindow->findChildren<QToolBar *>();
for (int i = 0; i < toolbars.size(); ++i) {
QToolBar *toolbar = toolbars.at(i);
if (toolBarArea(toolbar) == Qt::TopToolBarArea) {
removeWidget(toolbar); // Do this here, because we are in an in-between state.
layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar);
}
}
syncUnifiedToolbarVisibility();
}
layoutState.mainWindow->setUpdatesEnabled(true);
}
void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar)
{
// This layering could go on to one more level, but I decided to stop here.
// The HIToolbar and NSToolbar APIs are fairly similar as you will see.
if (toolbar == 0)
return;
QToolBarLayout *toolbarLayout = static_cast<QToolBarLayout *>(toolbar->layout());
toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(),
toolbar->maximumSize()));
if (toolbarLayout->hasExpandFlag() == false)
toolbar->setMaximumSize(toolbar->sizeHint());
toolbar->setMovable(false);
toolbarLayout->setUsePopupMenu(true);
// Make the toolbar a child of the mainwindow to avoid creating a window.
toolbar->setParent(layoutState.mainWindow);
toolbar->createWinId(); // Now create the OSViewRef.
layoutState.mainWindow->createWinId();
OSWindowRef window = qt_mac_window_for(layoutState.mainWindow);
int beforeIndex = qtoolbarsInUnifiedToolbarList.indexOf(before);
if (beforeIndex == -1)
beforeIndex = qtoolbarsInUnifiedToolbarList.size();
int toolbarIndex = qtoolbarsInUnifiedToolbarList.indexOf(toolbar);
#ifndef QT_MAC_USE_COCOA
HIToolbarRef macToolbar = NULL;
if ((GetWindowToolbar(window, &macToolbar) == noErr) && !macToolbar) {
HIToolbarCreate(kQMainWindowMacToolbarID,
kHIToolbarItemAllowDuplicates, &macToolbar);
InstallEventHandler(HIObjectGetEventTarget(static_cast<HIToolbarRef>(macToolbar)),
QMainWindowLayout::qtmacToolbarDelegate, GetEventTypeCount(kToolbarEvents),
kToolbarEvents, this, 0);
HIToolbarSetDisplaySize(macToolbar, kHIToolbarDisplaySizeNormal);
HIToolbarSetDisplayMode(macToolbar, kHIToolbarDisplayModeIconOnly);
macWindowToolbarSet(window, macToolbar);
if (layoutState.mainWindow->isVisible())
macWindowToolbarShow(layoutState.mainWindow, true);
CFRelease(macToolbar);
}
#else
QMacCocoaAutoReleasePool pool;
NSToolbar *macToolbar = [window toolbar];
if (macToolbar == nil) {
macToolbar = [[NSToolbar alloc] initWithIdentifier:(NSString *)kQMainWindowMacToolbarID];
[macToolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
[macToolbar setSizeMode:NSToolbarSizeModeRegular];
[macToolbar setDelegate:[[QCocoaToolBarDelegate alloc] initWithMainWindowLayout:this]];
[window setToolbar:macToolbar];
[macToolbar release];
}
#endif
if (toolbarIndex != -1) {
qtoolbarsInUnifiedToolbarList.removeAt(toolbarIndex);
#ifndef QT_MAC_USE_COCOA
HIToolbarRemoveItemAtIndex(macToolbar, toolbarIndex);
#else
[macToolbar removeItemAtIndex:toolbarIndex];
#endif
}
qtoolbarsInUnifiedToolbarList.insert(beforeIndex, toolbar);
#ifndef QT_MAC_USE_COCOA
QCFType<HIToolbarItemRef> outItem;
const QObject *stupidArray[] = { toolbar, this };
QCFType<CFArrayRef> array = CFArrayCreate(0, reinterpret_cast<const void **>(&stupidArray),
2, 0);
HIToolbarCreateItemWithIdentifier(macToolbar, kQToolBarHIToolbarIdentifier,
array, &outItem);
HIToolbarInsertItemAtIndex(macToolbar, outItem, beforeIndex);
#else
NSString *toolbarID = kQToolBarNSToolbarIdentifier;
toolbarID = [toolbarID stringByAppendingFormat:@"%p", toolbar];
cocoaItemIDToToolbarHash.insert(qt_mac_NSStringToQString(toolbarID), toolbar);
[macToolbar insertItemWithItemIdentifier:toolbarID atIndex:beforeIndex];
#endif
}
void QMainWindowLayout::removeFromMacToolbar(QToolBar *toolbar)
{
QHash<void *, QToolBar *>::iterator it = unifiedToolbarHash.begin();
while (it != unifiedToolbarHash.end()) {
if (it.value() == toolbar) {
// Rescue our HIView and set it on the mainWindow again.
bool saveVisible = !toolbar->isHidden();
toolbar->setParent(0);
toolbar->setParent(parentWidget());
toolbar->setVisible(saveVisible);
ToolBarSaveState saveState = toolbarSaveState.value(toolbar);
static_cast<QToolBarLayout *>(toolbar->layout())->setUsePopupMenu(false);
toolbar->setMovable(saveState.movable);
toolbar->setMaximumSize(saveState.maximumSize);
toolbarSaveState.remove(toolbar);
#ifndef QT_MAC_USE_COCOA
HIToolbarItemRef item = static_cast<HIToolbarItemRef>(it.key());
HIToolbarRemoveItemAtIndex(HIToolbarItemGetToolbar(item),
toolbarItemsCopy.indexOf(item));
#else
NSToolbarItem *item = static_cast<NSToolbarItem *>(it.key());
[[qt_mac_window_for(layoutState.mainWindow->window()) toolbar]
removeItemAtIndex:toolbarItemsCopy.indexOf(item)];
#endif
break;
}
++it;
}
}
void QMainWindowLayout::cleanUpMacToolbarItems()
{
#ifdef QT_MAC_USE_COCOA
QMacCocoaAutoReleasePool pool;
#endif
for (int i = 0; i < toolbarItemsCopy.size(); ++i) {
#ifdef QT_MAC_USE_COCOA
NSToolbarItem *item = static_cast<NSToolbarItem *>(toolbarItemsCopy.at(i));
[item setView:0];
#endif
CFRelease(toolbarItemsCopy.at(i));
}
toolbarItemsCopy.clear();
unifiedToolbarHash.clear();
#ifdef QT_MAC_USE_COCOA
OSWindowRef window = qt_mac_window_for(layoutState.mainWindow);
NSToolbar *macToolbar = [window toolbar];
if (macToolbar) {
[[macToolbar delegate] release];
[macToolbar setDelegate:nil];
}
#endif
}
void QMainWindowLayout::fixSizeInUnifiedToolbar(QToolBar *tb) const
{
#ifdef QT_MAC_USE_COCOA
QHash<void *, QToolBar *>::const_iterator it = unifiedToolbarHash.constBegin();
NSToolbarItem *item = nil;
while (it != unifiedToolbarHash.constEnd()) {
if (tb == it.value()) {
item = static_cast<NSToolbarItem *>(it.key());
break;
}
++it;
}
if (item) {
QMacCocoaAutoReleasePool pool;
QWidgetItem layoutItem(tb);
QSize size = layoutItem.maximumSize();
NSSize nssize = NSMakeSize(size.width(), size.height() - 2);
[item setMaxSize:nssize];
size = layoutItem.minimumSize();
nssize.width = size.width();
nssize.height = size.height() - 2;
[item setMinSize:nssize];
}
#else
Q_UNUSED(tb);
#endif
}
void QMainWindowLayout::syncUnifiedToolbarVisibility()
{
if (blockVisiblityCheck)
return;
Q_ASSERT(layoutState.mainWindow->unifiedTitleAndToolBarOnMac());
bool show = false;
const int ToolBarCount = qtoolbarsInUnifiedToolbarList.count();
for (int i = 0; i < ToolBarCount; ++i) {
if (qtoolbarsInUnifiedToolbarList.at(i)->isVisible()) {
show = true;
break;
}
}
macWindowToolbarShow(layoutState.mainWindow, show);
}
QT_END_NAMESPACE