/* * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "KWQObject.h" #import "KWQVariant.h" #import "KWQAssertions.h" // The Foundation-level Cocoa calls here (NSTimer, NSDate, NSArray, // NSDictionary) should be exception-free, so no need to block // exceptions. const QObject *QObject::_sender; bool QObject::_defersTimers; static CFMutableDictionaryRef timerDictionaries; static CFMutableDictionaryRef allPausedTimers; static NSMutableArray *deferredTimers; static bool deferringTimers; @interface KWQObjectTimerTarget : NSObject { QObject *target; int timerId; NSTimeInterval remainingTime; } - initWithQObject:(QObject *)object timerId:(int)timerId; - (void)timerFired; @end KWQSignal *QObject::findSignal(const char *signalName) const { for (KWQSignal *signal = _signalListHead; signal; signal = signal->_next) { if (KWQNamesMatch(signalName, signal->_name)) { return signal; } } return 0; } void QObject::connect(const QObject *sender, const char *signalName, const QObject *receiver, const char *member) { // FIXME: Assert that sender is not NULL rather than doing the if statement. if (!sender) { return; } KWQSignal *signal = sender->findSignal(signalName); if (!signal) { #if !ERROR_DISABLED if (1 && !KWQNamesMatch(member, SIGNAL(setStatusBarText(const QString &))) && !KWQNamesMatch(member, SLOT(slotHistoryChanged())) && !KWQNamesMatch(member, SLOT(slotJobPercent(KIO::Job *, unsigned long))) && !KWQNamesMatch(member, SLOT(slotJobSpeed(KIO::Job *, unsigned long))) && !KWQNamesMatch(member, SLOT(slotScrollBarMoved())) && !KWQNamesMatch(member, SLOT(slotShowDocument(const QString &, const QString &))) && !KWQNamesMatch(member, SLOT(slotViewCleared())) // FIXME: Should implement this one! ) ERROR("connecting member %s to signal %s, but that signal was not found", member, signalName); #endif return; } signal->connect(KWQSlot(const_cast<QObject *>(receiver), member)); } void QObject::disconnect(const QObject *sender, const char *signalName, const QObject *receiver, const char *member) { // FIXME: Assert that sender is not NULL rather than doing the if statement. if (!sender) return; KWQSignal *signal = sender->findSignal(signalName); if (!signal) { // FIXME: ERROR return; } signal->disconnect(KWQSlot(const_cast<QObject *>(receiver), member)); } KWQObjectSenderScope::KWQObjectSenderScope(const QObject *o) : _savedSender(QObject::_sender) { QObject::_sender = o; } KWQObjectSenderScope::~KWQObjectSenderScope() { QObject::_sender = _savedSender; } QObject::QObject(QObject *parent, const char *name) : _signalListHead(0), _signalsBlocked(false) , _destroyed(this, SIGNAL(destroyed())) , _eventFilterObject(0) { _guardedPtrDummyList.append(this); } QObject::~QObject() { _destroyed.call(); ASSERT(_signalListHead == &_destroyed); killTimers(); } void QObject::timerEvent(QTimerEvent *te) { } bool QObject::event(QEvent *) { return false; } void QObject::pauseTimer (int _timerId, const void *key) { NSMutableDictionary *timers = (NSMutableDictionary *)CFDictionaryGetValue(timerDictionaries, this); NSNumber *timerId = [NSNumber numberWithInt:_timerId]; NSTimer *timer = (NSTimer *)[timers objectForKey:timerId]; if ([timer isValid]){ KWQObjectTimerTarget *target = (KWQObjectTimerTarget *)[timer userInfo]; if (target){ NSDate *fireDate = [timer fireDate]; NSTimeInterval remainingTime = [fireDate timeIntervalSinceDate: [NSDate date]]; if (remainingTime < 0) remainingTime = DBL_EPSILON; if (allPausedTimers == NULL) { // The global targets dictionary itself leaks, but the contents are removed // when each timer fires or is killed. allPausedTimers = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks); } NSMutableArray *pausedTimers = (NSMutableArray *)CFDictionaryGetValue(allPausedTimers, key); if (pausedTimers == nil) { pausedTimers = [[NSMutableArray alloc] init]; CFDictionarySetValue(allPausedTimers, key, pausedTimers); [pausedTimers release]; } target->remainingTime = remainingTime; [pausedTimers addObject:target]; [timer invalidate]; [timers removeObjectForKey:timerId]; } } } void QObject::_addTimer(NSTimer *timer, int _timerId) { NSMutableDictionary *timers = (NSMutableDictionary *)CFDictionaryGetValue(timerDictionaries, this); if (timers == nil) { timers = [[NSMutableDictionary alloc] init]; CFDictionarySetValue(timerDictionaries, this, timers); [timers release]; } [timers setObject:timer forKey:[NSNumber numberWithInt:_timerId]]; } static int nextTimerID = 1; void QObject::clearPausedTimers (const void *key) { if (allPausedTimers) CFDictionaryRemoveValue(allPausedTimers, key); } void QObject::resumeTimers (const void *key, QObject *_target) { if (allPausedTimers == NULL) { return; } int maxId = MAX(0, nextTimerID); NSMutableArray *pausedTimers = (NSMutableArray *)CFDictionaryGetValue(allPausedTimers, key); if (pausedTimers == nil) return; int count = [pausedTimers count]; while (count--){ KWQObjectTimerTarget *target = [pausedTimers objectAtIndex: count]; target->target = _target; NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:target->remainingTime target:target selector:@selector(timerFired) userInfo:target repeats:YES]; [pausedTimers removeLastObject]; maxId = MAX (maxId, target->timerId); _addTimer (timer, target->timerId); } nextTimerID = maxId+1; CFDictionaryRemoveValue(allPausedTimers, key); } int QObject::startTimer(int milliseconds) { if (timerDictionaries == NULL) { // The global timers dictionary itself lives forever, but the contents are removed // when each timer fires or is killed. timerDictionaries = CFDictionaryCreateMutable(NULL, 0, NULL, &kCFTypeDictionaryValueCallBacks); } NSMutableDictionary *timers = (NSMutableDictionary *)CFDictionaryGetValue(timerDictionaries, this); if (timers == nil) { timers = [[NSMutableDictionary alloc] init]; CFDictionarySetValue(timerDictionaries, this, timers); [timers release]; } KWQObjectTimerTarget *target = [[KWQObjectTimerTarget alloc] initWithQObject:this timerId:nextTimerID]; NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:milliseconds / 1000.0 target:target selector:@selector(timerFired) userInfo:target repeats:YES]; [target release]; _addTimer (timer, nextTimerID); return nextTimerID++; } void QObject::killTimer(int _timerId) { if (_timerId == 0) { return; } if (timerDictionaries == NULL) { return; } NSMutableDictionary *timers = (NSMutableDictionary *)CFDictionaryGetValue(timerDictionaries, this); NSNumber *timerId = [NSNumber numberWithInt:_timerId]; NSTimer *timer = (NSTimer *)[timers objectForKey:timerId]; // Only try to remove the timer is it hasn't fired (and is therefore valid). It is NOT // permissible to reference a timer's userInfo if it is invalid. if ([timer isValid]){ [deferredTimers removeObject:(KWQObjectTimerTarget *)[timer userInfo]]; [timer invalidate]; } [timers removeObjectForKey:timerId]; } void QObject::killTimers() { if (timerDictionaries == NULL) { return; } NSMutableDictionary *timers = (NSMutableDictionary *)CFDictionaryGetValue(timerDictionaries, this); if (timers == nil) { return; } NSEnumerator *e = [timers keyEnumerator]; NSNumber *timerId; while ((timerId = [e nextObject]) != nil) { killTimer([timerId intValue]); } CFDictionaryRemoveValue(timerDictionaries, this); } void QObject::setDefersTimers(bool defers) { if (defers) { _defersTimers = true; deferringTimers = true; [NSObject cancelPreviousPerformRequestsWithTarget:[KWQObjectTimerTarget class]]; return; } if (_defersTimers) { _defersTimers = false; if (deferringTimers) { [KWQObjectTimerTarget performSelector:@selector(stopDeferringTimers) withObject:nil afterDelay:0]; } } } @implementation KWQObjectTimerTarget - initWithQObject:(QObject *)qo timerId:(int)t { [super init]; target = qo; timerId = t; return self; } - (void)sendTimerEvent { QTimerEvent event(timerId); target->timerEvent(&event); } - (void)timerFired { if (deferringTimers) { if (deferredTimers == nil) { deferredTimers = [[NSMutableArray alloc] init]; } if (![deferredTimers containsObject:self]) { [deferredTimers addObject:self]; } } else { [self sendTimerEvent]; } } + (void)stopDeferringTimers { ASSERT(deferringTimers); while ([deferredTimers count] != 0) { // remove before sending the timer event, in case the timer // callback cancels the timer - we don't want to remove too // much in that case. KWQObjectTimerTarget *timerTarget = [deferredTimers objectAtIndex:0]; [timerTarget retain]; [deferredTimers removeObjectAtIndex:0]; [timerTarget sendTimerEvent]; [timerTarget release]; } deferringTimers = false; } @end bool QObject::inherits(const char *className) const { if (strcmp(className, "KHTMLPart") == 0) { return isKHTMLPart(); } if (strcmp(className, "KHTMLView") == 0) { return isKHTMLView(); } if (strcmp(className, "KParts::Factory") == 0) { return false; } if (strcmp(className, "KParts::ReadOnlyPart") == 0) { return isKPartsReadOnlyPart(); } if (strcmp(className, "QFrame") == 0) { return isQFrame(); } if (strcmp(className, "QScrollView") == 0) { return isQScrollView(); } ERROR("class name %s not recognized", className); return false; } bool QObject::isKHTMLPart() const { return false; } bool QObject::isKHTMLView() const { return false; } bool QObject::isKPartsReadOnlyPart() const { return false; } bool QObject::isQFrame() const { return false; } bool QObject::isQScrollView() const { return false; }