/*
* 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 "KWQTextEdit.h"
#import "KWQAssertions.h"
#import "KWQExceptions.h"
#import "KWQLineEdit.h"
#import "KWQTextArea.h"
QTextEdit::QTextEdit(QWidget *parent)
: _clicked(this, SIGNAL(clicked()))
, _textChanged(this, SIGNAL(textChanged()))
{
KWQ_BLOCK_EXCEPTIONS;
KWQTextArea *textView = [[KWQTextArea alloc] initWithQTextEdit:this];
setView(textView);
[textView release];
KWQ_UNBLOCK_EXCEPTIONS;
}
QTextEdit::~QTextEdit()
{
KWQTextArea *textArea = (KWQTextArea *)getView();
[textArea detachQTextEdit];
}
void QTextEdit::setText(const QString &string)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
[textView setText:string.getNSString()];
KWQ_UNBLOCK_EXCEPTIONS;
}
QString QTextEdit::text() const
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
return QString::fromNSString([textView text]);
KWQ_UNBLOCK_EXCEPTIONS;
return QString();
}
QString QTextEdit::textWithHardLineBreaks() const
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
return QString::fromNSString([textView textWithHardLineBreaks]);
KWQ_UNBLOCK_EXCEPTIONS;
return QString();
}
void QTextEdit::getCursorPosition(int *paragraph, int *index) const
{
KWQTextArea *textView = (KWQTextArea *)getView();
if (index)
*index = 0;
if (paragraph)
*paragraph = 0;
KWQ_BLOCK_EXCEPTIONS;
[textView getCursorPositionAsIndex:index inParagraph:paragraph];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::setCursorPosition(int paragraph, int index)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
[textView setCursorPositionToIndex:index inParagraph:paragraph];
KWQ_UNBLOCK_EXCEPTIONS;
}
QTextEdit::WrapStyle QTextEdit::wordWrap() const
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
return [textView wordWrap] ? WidgetWidth : NoWrap;
KWQ_UNBLOCK_EXCEPTIONS;
return NoWrap;
}
void QTextEdit::setWordWrap(WrapStyle style)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
[textView setWordWrap:style == WidgetWidth];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::setScrollBarModes(ScrollBarMode hMode, ScrollBarMode vMode)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
#if !BUILDING_ON_PANTHER
// this declaration must be inside the KWQ_BLOCK_EXCEPTIONS block or the deployment build fails
bool autohides = hMode == Auto || vMode == Auto;
ASSERT(!autohides || hMode != AlwaysOn);
ASSERT(!autohides || vMode != AlwaysOn);
#endif
[textView setHasHorizontalScroller:hMode != AlwaysOff];
[textView setHasVerticalScroller:vMode != AlwaysOff];
#if !BUILDING_ON_PANTHER
// Bugs 3890352 and 4005435 are the reason we can't handle auto-hiding on Panther.
// Basically, the text machinery seems to be able to handle the case where new text
// causes the text view to become more narrow on Tiger, but not on Panther.
[textView setAutohidesScrollers:autohides];
#endif
KWQ_UNBLOCK_EXCEPTIONS;
}
bool QTextEdit::isReadOnly() const
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
return ![textView isEditable];
KWQ_UNBLOCK_EXCEPTIONS;
return false;
}
void QTextEdit::setReadOnly(bool flag)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
[textView setEditable:!flag];
KWQ_UNBLOCK_EXCEPTIONS;
}
bool QTextEdit::isDisabled() const
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
return ![textView isEnabled];
KWQ_UNBLOCK_EXCEPTIONS;
return false;
}
void QTextEdit::setDisabled(bool flag)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
[textView setEnabled:!flag];
KWQ_UNBLOCK_EXCEPTIONS;
}
long QTextEdit::selectionStart()
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
NSRange range = [textView selectedRange];
if (range.location == NSNotFound)
return 0;
return range.location;
KWQ_UNBLOCK_EXCEPTIONS;
return 0;
}
long QTextEdit::selectionEnd()
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
NSRange range = [textView selectedRange];
if (range.location == NSNotFound)
return 0;
return (range.location + range.length);
KWQ_UNBLOCK_EXCEPTIONS;
return 0;
}
void QTextEdit::setSelectionStart(long start)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
NSRange range = [textView selectedRange];
if (range.location == NSNotFound) {
range.location = 0;
range.length = 0;
}
// coerce start to a valid value
long maxLength = [[textView text] length];
long newStart = start;
if (newStart < 0)
newStart = 0;
if (newStart > maxLength)
newStart = maxLength;
if ((unsigned)newStart < range.location + range.length) {
// If we're expanding or contracting, but not collapsing the selection
range.length += range.location - newStart;
range.location = newStart;
} else {
// ok, we're collapsing the selection
range.location = newStart;
range.length = 0;
}
[textView setSelectedRange:range];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::setSelectionEnd(long end)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
NSRange range = [textView selectedRange];
if (range.location == NSNotFound) {
range.location = 0;
range.length = 0;
}
// coerce end to a valid value
long maxLength = [[textView text] length];
long newEnd = end;
if (newEnd < 0)
newEnd = 0;
if (newEnd > maxLength)
newEnd = maxLength;
if ((unsigned)newEnd >= range.location) {
// If we're just changing the selection length, but not location..
range.length = newEnd - range.location;
} else {
// ok, we've collapsed the selection and are moving it
range.location = newEnd;
range.length = 0;
}
[textView setSelectedRange:range];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::selectAll()
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
[textView selectAll];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::setSelectionRange(long start, long length)
{
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
long newStart = start;
long newLength = length;
if (newStart < 0) {
// truncate the length by the negative start
newLength = length + newStart;
newStart = 0;
}
if (newLength < 0) {
newLength = 0;
}
int maxlen = [[textView text] length];
if (newStart > maxlen) {
newStart = maxlen;
}
if (newStart + newLength > maxlen) {
newLength = maxlen - newStart;
}
NSRange r;
r.location = newStart;
r.length = newLength;
[textView setSelectedRange:r];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::setFont(const QFont &font)
{
QWidget::setFont(font);
KWQTextArea *textView = (KWQTextArea *)getView();
KWQ_BLOCK_EXCEPTIONS;
[textView setFont:font.getNSFont()];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::clicked()
{
_clicked.call();
}
void QTextEdit::setAlignment(AlignmentFlags alignment)
{
KWQ_BLOCK_EXCEPTIONS;
KWQTextArea *textArea = static_cast<KWQTextArea *>(getView());
[textArea setAlignment:KWQNSTextAlignmentForAlignmentFlags(alignment)];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::setLineHeight(int lineHeight)
{
KWQ_BLOCK_EXCEPTIONS;
KWQTextArea *textArea = static_cast<KWQTextArea *>(getView());
[textArea setLineHeight:lineHeight];
KWQ_UNBLOCK_EXCEPTIONS;
}
void QTextEdit::setWritingDirection(QPainter::TextDirection direction)
{
KWQ_BLOCK_EXCEPTIONS;
KWQTextArea *textArea = static_cast<KWQTextArea *>(getView());
[textArea setBaseWritingDirection:(direction == QPainter::RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight)];
KWQ_UNBLOCK_EXCEPTIONS;
}
QSize QTextEdit::sizeWithColumnsAndRows(int numColumns, int numRows) const
{
KWQTextArea *textArea = static_cast<KWQTextArea *>(getView());
NSSize size = {0,0};
KWQ_BLOCK_EXCEPTIONS;
size = [textArea sizeWithColumns:numColumns rows:numRows];
KWQ_UNBLOCK_EXCEPTIONS;
return QSize((int)ceil(size.width), (int)ceil(size.height));
}
QWidget::FocusPolicy QTextEdit::focusPolicy() const
{
FocusPolicy policy = QScrollView::focusPolicy();
return policy == TabFocus ? StrongFocus : policy;
}
bool QTextEdit::checksDescendantsForFocus() const
{
return true;
}
void QTextEdit::setPalette(const QPalette &palette)
{
QWidget::setPalette(palette);
KWQTextArea *textArea = static_cast<KWQTextArea *>(getView());
KWQ_BLOCK_EXCEPTIONS;
// Below is a workaround for the following AppKit bug which causes transparent backgrounds to be
// drawn opaque <rdar://problem/3142730>. Without this workaround, some textareas would be drawn with black backgrounds
// as described in <rdar://problem/3854383>. We now call setDrawsBackground:NO when the background color is completely
// transparent. This does not solve the problem for translucent background colors for textareas <rdar://problem/3865161>.
[textArea setTextColor:palette.foreground().getNSColor()];
QColor background = palette.background();
if (!background.isValid())
background = Qt::white;
[textArea setBackgroundColor:background.getNSColor()];
[textArea setDrawsBackground:background.alpha() != 0];
KWQ_UNBLOCK_EXCEPTIONS;
}