~ubuntu-branches/ubuntu/jaunty/psi/jaunty

« back to all changes in this revision

Viewing changes to src/msgmle.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2008-04-14 18:57:30 UTC
  • mfrom: (2.1.9 hardy)
  • Revision ID: james.westby@ubuntu.com-20080414185730-528re3zp0m2hdlhi
Tags: 0.11-8
* added CONFIG -= link_prl to .pro files and removed dependencies
  which are made unnecessary by this change
* Fix segfault when closing last chat tab with qt4.4
  (This is from upstream svn, rev. 1101) (Closes: Bug#476122)

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 *
19
19
 */
20
20
 
21
 
#include<qapplication.h>
22
 
#include<qlayout.h>
23
 
#include<qtimer.h>
 
21
#include <QAbstractTextDocumentLayout>
 
22
#include <QAction>
 
23
#include <QApplication>
 
24
#include <QDesktopWidget>
 
25
#include <QEvent>
 
26
#include <QKeyEvent>
 
27
#include <QLayout>
 
28
#include <QMenu>
 
29
#include <QResizeEvent>
 
30
#include <QScrollBar>
 
31
#include <QTextCharFormat>
 
32
#include <QTextDocument>
 
33
#include <QTimer>
24
34
 
25
 
#include"common.h"
26
 
#include"msgmle.h"
 
35
#include "common.h"
 
36
#include "msgmle.h"
 
37
#include "shortcutmanager.h"
 
38
#include "spellhighlighter.h"
 
39
#include "spellchecker.h"
 
40
#include "psioptions.h"
27
41
 
28
42
//----------------------------------------------------------------------------
29
43
// ChatView
30
44
//----------------------------------------------------------------------------
31
 
ChatView::ChatView(QWidget *parent, const char *name)
32
 
: PsiTextView(parent, name)
 
45
ChatView::ChatView(QWidget *parent)
 
46
        : PsiTextView(parent)
 
47
        , dialog_(0)
33
48
{
34
 
        setWordWrap(WidgetWidth);
35
 
        setWrapPolicy(AtWordOrDocumentBoundary);
 
49
        setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
36
50
 
37
 
        setTextFormat(RichText);
38
51
        setReadOnly(true);
39
52
        setUndoRedoEnabled(false);
40
 
        setHScrollBarMode(QScrollView::AlwaysOff);
 
53
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
41
54
 
42
55
#ifndef Q_WS_X11        // linux has this feature built-in
43
 
        connect(this, SIGNAL(copyAvailable(bool)), SLOT(autoCopy(bool)));
 
56
        connect(this, SIGNAL(selectionChanged()), SLOT(autoCopy()));
 
57
        connect(this, SIGNAL(cursorPositionChanged()), SLOT(autoCopy()));
44
58
#endif
45
 
 
46
59
}
47
60
 
48
61
ChatView::~ChatView()
49
62
{
50
63
}
51
64
 
 
65
void ChatView::setDialog(QWidget* dialog)
 
66
{
 
67
        dialog_ = dialog;
 
68
}
 
69
 
 
70
QSize ChatView::sizeHint() const
 
71
{
 
72
        return minimumSizeHint();
 
73
}
 
74
 
52
75
bool ChatView::focusNextPrevChild(bool next)
53
76
{
54
77
        return QWidget::focusNextPrevChild(next);
56
79
 
57
80
void ChatView::keyPressEvent(QKeyEvent *e)
58
81
{
59
 
        if(e->key() == Key_Escape)
60
 
                e->ignore();
 
82
        if(dialog_) {
 
83
                QKeySequence k(e->key() + (e->modifiers() & ~Qt::KeypadModifier));
 
84
 
 
85
                // Temporary workaround for what i think is a Qt bug
 
86
                if(ShortcutManager::instance()->shortcuts("common.close").contains(k)
 
87
                        || ShortcutManager::instance()->shortcuts("message.send").contains(k)) {
 
88
                        e->ignore();
 
89
                        return;
 
90
                }
 
91
 
 
92
                // Ignore registered key sequences (and pass them up)
 
93
                foreach(QAction* act, dialog_->actions()) {
 
94
                        foreach(QKeySequence keyseq, act->shortcuts()) {
 
95
                                if(!keyseq.isEmpty() && keyseq.matches(k) == QKeySequence::ExactMatch) {
 
96
                                        e->ignore();
 
97
                                        //act->trigger();
 
98
                                        return;
 
99
                                }
 
100
                        }
 
101
                }
 
102
        }
 
103
 
 
104
/*      if(e->key() == Qt::Key_Escape)
 
105
                e->ignore(); 
61
106
#ifdef Q_WS_MAC
62
 
        else if(e->key() == Key_W && e->state() & ControlButton)
63
 
                e->ignore();
 
107
        else if(e->key() == Qt::Key_W && e->modifiers() & Qt::ControlModifier)
 
108
                e->ignore();
 
109
        else if(e->key() == Qt::Key_C && (e->state() & Qt::ControlButton) && !hasSelectedText())
 
110
                e->ignore();
 
111
        else
64
112
#endif
65
 
        else if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
66
 
                e->ignore();
67
 
        else if(e->key() == Key_H && (e->state() & ControlButton))
68
 
                e->ignore();
69
 
        else if(e->key() == Key_I && (e->state() & ControlButton))
70
 
                e->ignore();
71
 
        else if(e->key() == Key_M && (e->state() & ControlButton)) // newline
 
113
        else if(e->key() == Qt::Key_Return && ((e->modifiers() & Qt::ControlModifier) || (e->modifiers() & Qt::AltModifier)) )
 
114
                e->ignore();
 
115
        else if(e->key() == Qt::Key_H && (e->modifiers() & Qt::ControlModifier))
 
116
                e->ignore();
 
117
        else if(e->key() == Qt::Key_I && (e->modifiers() & Qt::ControlModifier))
 
118
                e->ignore(); */
 
119
        /*else*/ if(e->key() == Qt::Key_M && (e->modifiers() & Qt::ControlModifier) && !isReadOnly()) // newline 
72
120
                insert("\n");
73
 
        else if(e->key() == Key_U && (e->state() & ControlButton))
74
 
                setText("");
 
121
/*      else if(e->key() == Qt::Key_U && (e->modifiers() & Qt::ControlModifier) && !isReadOnly())
 
122
                setText(""); */
75
123
        else
76
 
                QTextEdit::keyPressEvent(e);
77
 
}
78
 
 
79
 
void ChatView::resizeEvent(QResizeEvent *e)
80
 
{
81
 
        // This fixes flyspray #45
82
 
        if(contentsY() == contentsHeight() - visibleHeight())
 
124
                PsiTextView::keyPressEvent(e);
 
125
}
 
126
 
 
127
/**
 
128
 * Copies any selected text to the clipboard
 
129
 * if autoCopy is enabled and ChatView is in read-only mode.
 
130
 */
 
131
void ChatView::autoCopy()
 
132
{
 
133
        if (isReadOnly() && option.autoCopy) {
 
134
                copy();
 
135
        }
 
136
}
 
137
 
 
138
/**
 
139
 * Handle KeyPress events that happen in ChatEdit widget. This is used
 
140
 * to 'fix' the copy shortcut.
 
141
 * \param object object that should receive the event
 
142
 * \param event received event
 
143
 * \param chatEdit pointer to the dialog's ChatEdit widget that receives user input
 
144
 */
 
145
bool ChatView::handleCopyEvent(QObject *object, QEvent *event, ChatEdit *chatEdit)
 
146
{
 
147
        if (object == chatEdit && event->type() == QEvent::KeyPress) {
 
148
                QKeyEvent *e = (QKeyEvent *)event;
 
149
                if ((e->key() == Qt::Key_C && (e->modifiers() & Qt::ControlModifier)) ||
 
150
                    (e->key() == Qt::Key_Insert && (e->modifiers() & Qt::ControlModifier)))
 
151
                {
 
152
                        if (!chatEdit->textCursor().hasSelection() &&
 
153
                             this->textCursor().hasSelection()) 
 
154
                        {
 
155
                                this->copy();
 
156
                                return true;
 
157
                        }
 
158
                }
 
159
        }
 
160
        
 
161
        return false;
 
162
}
 
163
 
 
164
void ChatView::appendText(const QString &text)
 
165
{
 
166
        bool doScrollToBottom = atBottom();
 
167
        
 
168
        // prevent scrolling back to selected text when 
 
169
        // restoring selection
 
170
        int scrollbarValue = verticalScrollBar()->value();
 
171
        
 
172
        PsiTextView::appendText(text);
 
173
        
 
174
        if (doScrollToBottom)
83
175
                scrollToBottom();
84
 
 
85
 
        QTextEdit::resizeEvent(e);
 
176
        else
 
177
                verticalScrollBar()->setValue(scrollbarValue);
86
178
}
87
179
 
88
 
/*!
89
 
        Copies any selected text (from selection 0) to the clipboard
90
 
        if option.autoCopy is TRUE, \a copyAvailable is TRUE
91
 
        and ChatView is in read-only mode.
92
 
        In any other case it does nothing.
93
 
 
94
 
        This slot is connected with copyAvailable(bool) signal
95
 
        in ChatView's constructor.
96
 
 
97
 
        \sa copyAvailable()
98
 
*/
99
 
 
100
 
void ChatView::autoCopy(bool copyAvailable)
 
180
/**
 
181
 * \brief Common function for ChatDlg and GCMainDlg. FIXME: Extract common
 
182
 * chat window from both dialogs and move this function to that class.
 
183
 */
 
184
QString ChatView::formatTimeStamp(const QDateTime &time)
101
185
{
102
 
        if ( isReadOnly() && copyAvailable && option.autoCopy ) {
103
 
                copy();
104
 
        }
 
186
        // TODO: provide an option for user to customize
 
187
        // time stamp format
 
188
        return QString().sprintf("%02d:%02d:%02d", time.time().hour(), time.time().minute(), time.time().second());;
105
189
}
106
190
 
107
191
//----------------------------------------------------------------------------
108
192
// ChatEdit
109
193
//----------------------------------------------------------------------------
110
 
ChatEdit::ChatEdit(QWidget *parent, const char *name)
111
 
: PsiTextView(parent, name)
 
194
ChatEdit::ChatEdit(QWidget *parent)
 
195
        : QTextEdit(parent)
 
196
        , dialog_(0)
 
197
        , check_spelling_(false)
 
198
        , spellhighlighter_(0)
112
199
{
113
 
        setWordWrap(QTextEdit::WidgetWidth);
 
200
        setWordWrapMode(QTextOption::WordWrap);
 
201
        setAcceptRichText(false);
114
202
 
115
203
        setReadOnly(false);
116
204
        setUndoRedoEnabled(true);
117
205
 
118
 
        setTextFormat(PlainText);
119
206
        setMinimumHeight(48);
 
207
 
 
208
        previous_position_ = 0;
 
209
        setCheckSpelling(checkSpellingGloballyEnabled());
 
210
        connect(PsiOptions::instance(),SIGNAL(optionChanged(const QString&)),SLOT(optionsChanged()));
120
211
}
121
212
 
122
213
ChatEdit::~ChatEdit()
123
214
{
 
215
        delete spellhighlighter_;
 
216
}
 
217
 
 
218
void ChatEdit::setDialog(QWidget* dialog)
 
219
{
 
220
        dialog_ = dialog;
 
221
}
 
222
 
 
223
QSize ChatEdit::sizeHint() const
 
224
{
 
225
        return minimumSizeHint();
 
226
}
 
227
 
 
228
bool ChatEdit::checkSpellingGloballyEnabled()
 
229
{
 
230
        return PsiOptions::instance()->getOption("options.ui.spell-check.enabled").toBool();
 
231
}
 
232
 
 
233
void ChatEdit::setCheckSpelling(bool b)
 
234
{
 
235
        check_spelling_ = b;
 
236
        if (check_spelling_) {
 
237
                if (!spellhighlighter_)
 
238
                        spellhighlighter_ = new SpellHighlighter(document());
 
239
        }
 
240
        else {
 
241
                delete spellhighlighter_;
 
242
                spellhighlighter_ = 0;
 
243
        }
124
244
}
125
245
 
126
246
bool ChatEdit::focusNextPrevChild(bool next)
130
250
 
131
251
void ChatEdit::keyPressEvent(QKeyEvent *e)
132
252
{
133
 
        if(e->key() == Key_Escape || (e->key() == Key_W && e->state() & ControlButton))
134
 
                e->ignore();
135
 
        else if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
136
 
                e->ignore();
137
 
        else if(e->key() == Key_M && (e->state() & ControlButton)) // newline
 
253
        if(dialog_) {
 
254
                QKeySequence k(e->key() + (e->modifiers() & ~Qt::KeypadModifier));
 
255
                // Temporary workaround for what i think is a Qt bug
 
256
                if(ShortcutManager::instance()->shortcuts("common.close").contains(k)
 
257
                        || ShortcutManager::instance()->shortcuts("chat.send").contains(k)) {
 
258
                        e->ignore();
 
259
                        return;
 
260
                }
 
261
 
 
262
                // Ignore registered key sequences (and pass them up)
 
263
                foreach(QAction* act, dialog_->actions()) {
 
264
                        foreach(QKeySequence keyseq, act->shortcuts()) {
 
265
                                if(!keyseq.isEmpty() && keyseq.matches(k) == QKeySequence::ExactMatch) {
 
266
                                        e->ignore();
 
267
                                        //act->trigger();
 
268
                                        return;
 
269
                                }
 
270
                        }
 
271
                }
 
272
        }
 
273
/*      if(e->key() == Qt::Key_Escape || (e->key() == Qt::Key_W && e->modifiers() & Qt::ControlModifier))
 
274
                e->ignore();
 
275
        else if(e->key() == Qt::Key_Return && 
 
276
               ((e->modifiers() & Qt::ControlModifier) 
 
277
#ifndef Q_WS_MAC
 
278
               || (e->modifiers() & Qt::AltModifier) 
 
279
#endif
 
280
               ))
 
281
                e->ignore();
 
282
        else if(e->key() == Qt::Key_M && (e->modifiers() & Qt::ControlModifier)) // newline
138
283
                insert("\n");
139
 
        else if(e->key() == Key_H && (e->state() & ControlButton)) // history
140
 
                e->ignore();
141
 
        else if(e->key() == Key_S && (e->state() & AltButton))
142
 
                e->ignore();
143
 
        else if(e->key() == Key_U && (e->state() & ControlButton))
 
284
        else if(e->key() == Qt::Key_H && (e->modifiers() & Qt::ControlModifier)) // history
 
285
                e->ignore();
 
286
        else  if(e->key() == Qt::Key_S && (e->modifiers() & Qt::AltModifier))
 
287
                e->ignore();
 
288
        else*/ if(e->key() == Qt::Key_U && (e->modifiers() & Qt::ControlModifier))
144
289
                setText("");
145
 
        else if((e->key() == Key_Return || e->key() == Key_Enter) && !(e->state() & ShiftButton) && option.chatSoftReturn)
146
 
                e->ignore();
147
 
        else if((e->key() == Key_PageUp || e->key() == Key_PageDown) && (e->state() & ShiftButton))
148
 
                e->ignore();
149
 
        else if((e->key() == Key_PageUp || e->key() == Key_PageDown) && (e->state() & ControlButton))
150
 
                e->ignore();
151
 
        else if(e->key() == Key_C && (e->state() & ControlButton) && !hasSelectedText())
152
 
                e->ignore();
 
290
/*      else if((e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) && !((e->modifiers() & Qt::ShiftModifier) || (e->modifiers() & Qt::AltModifier)) && option.chatSoftReturn)
 
291
                e->ignore();
 
292
        else if((e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown) && (e->modifiers() & Qt::ShiftModifier))
 
293
                e->ignore();
 
294
        else if((e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown) && (e->modifiers() & Qt::ControlModifier))
 
295
                e->ignore(); */
 
296
#ifdef Q_WS_MAC
 
297
        else if (e->key() == Qt::Key_QuoteLeft && e->modifiers() == Qt::ControlModifier) {
 
298
                e->ignore();
 
299
        }
 
300
#endif
153
301
        else
 
302
        {
154
303
                QTextEdit::keyPressEvent(e);
 
304
        }
 
305
}
 
306
 
 
307
/**
 
308
 * Work around Qt bug, that QTextEdit doesn't accept() the 
 
309
 * event, so it could result in another context menu popping
 
310
 * out after the first one.
 
311
 */
 
312
void ChatEdit::contextMenuEvent(QContextMenuEvent *e) 
 
313
{
 
314
        last_click_ = e->pos();
 
315
        if (check_spelling_ && textCursor().selectedText().isEmpty() && SpellChecker::instance()->available()) {
 
316
                // Check if the word under the cursor is misspelled
 
317
                QTextCursor tc = cursorForPosition(last_click_);
 
318
                tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
 
319
                tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
 
320
                QString selected_word = tc.selectedText();
 
321
                if (!selected_word.isEmpty() && !SpellChecker::instance()->isCorrect(selected_word)) {
 
322
                        QList<QString> suggestions = SpellChecker::instance()->suggestions(selected_word);
 
323
                        if (!suggestions.isEmpty() || SpellChecker::instance()->writable()) {
 
324
                                QMenu spell_menu;
 
325
                                if (!suggestions.isEmpty()) {
 
326
                                        foreach(QString suggestion, suggestions) {
 
327
                                                QAction* act_suggestion = spell_menu.addAction(suggestion);
 
328
                                                connect(act_suggestion,SIGNAL(triggered()),SLOT(applySuggestion()));
 
329
                                        }
 
330
                                        spell_menu.addSeparator();
 
331
                                }
 
332
                                if (SpellChecker::instance()->writable()) {
 
333
                                        QAction* act_add = spell_menu.addAction(tr("Add to dictionary"));
 
334
                                        connect(act_add,SIGNAL(triggered()),SLOT(addToDictionary()));
 
335
                                }
 
336
                                spell_menu.exec(QCursor::pos());
 
337
                                e->accept();
 
338
                                return;
 
339
                        }
 
340
                }
 
341
        }
 
342
 
 
343
        // Do normal menu
 
344
        QTextEdit::contextMenuEvent(e);
 
345
        e->accept();
 
346
}
 
347
 
 
348
/*!
 
349
 * \brief handles a click on a suggestion
 
350
 * \param the action is just the container which holds the suggestion.
 
351
 * 
 
352
 * This method is called by the framework whenever a user clicked on the child popupmenu
 
353
 * to select a suggestion for a missspelled word. It exchanges the missspelled word with the
 
354
 * suggestion which is the text of the QAction parameter.
 
355
 */
 
356
void ChatEdit::applySuggestion()
 
357
{
 
358
        QAction* act_suggestion = (QAction*) sender();
 
359
        int current_position = textCursor().position();
 
360
        
 
361
        // Replace the word
 
362
        QTextCursor     tc = cursorForPosition(last_click_);
 
363
        tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
 
364
        tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
 
365
        int old_length = tc.position() - tc.anchor();
 
366
        tc.insertText(act_suggestion->text());
 
367
        tc.clearSelection();
 
368
 
 
369
        // Put the cursor where it belongs
 
370
        int new_length = act_suggestion->text().length();
 
371
        tc.setPosition(current_position - old_length + new_length);
 
372
        setTextCursor(tc);
 
373
}
 
374
 
 
375
/*!
 
376
 * \brief handles a click on the add2dict action of the parent popupmenu
 
377
 * \param Never used bool parameter
 
378
 * 
 
379
 * The method sets the cursor to the last mouseclick position and looks for the word which is placed there.
 
380
 * This word is than added to the dictionary of aspell.
 
381
 */
 
382
void ChatEdit::addToDictionary()
 
383
{
 
384
        QTextCursor     tc = cursorForPosition(last_click_);
 
385
        int current_position = textCursor().position();
 
386
 
 
387
        // Get the selected word
 
388
        tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
 
389
        tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
 
390
        SpellChecker::instance()->add(tc.selectedText());
 
391
        
 
392
        // Put the cursor where it belongs
 
393
        tc.clearSelection();
 
394
        tc.setPosition(current_position);
 
395
        setTextCursor(tc);
 
396
}
 
397
 
 
398
void ChatEdit::optionsChanged()
 
399
{
 
400
        setCheckSpelling(checkSpellingGloballyEnabled());
155
401
}
156
402
 
157
403
//----------------------------------------------------------------------------
158
404
// LineEdit
159
405
//----------------------------------------------------------------------------
160
 
LineEdit::LineEdit( QWidget *parent, const char *name )
161
 
        : ChatEdit( parent, name )
 
406
LineEdit::LineEdit( QWidget *parent)
 
407
        : ChatEdit(parent)
162
408
{
163
 
        lastSize = QSize( 0, 0 );
164
 
        initialWindowGeometry = QRect( 0, 0, 0, 0 );
165
 
 
166
 
        QWidget *topParent = topLevelWidget();
167
 
        topParent->installEventFilter( this );
168
 
        moveTo = QPoint(topParent->x(), topParent->y());
169
 
 
170
 
        moveTimer = new QTimer( this );
171
 
        connect( moveTimer, SIGNAL(timeout()), SLOT(checkMoved()) );
172
 
 
173
 
        // LineEdit's size hint is to be vertically as small as possible
174
 
        setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum );
175
 
 
176
 
        setWrapPolicy( AtWordOrDocumentBoundary ); // no need for horizontal scrollbar with this
177
 
        setHScrollBarMode(QScrollView::AlwaysOff);
178
 
 
179
 
        setMinimumHeight(-1);
180
 
 
181
 
        connect( this, SIGNAL( textChanged() ), SLOT( recalculateSize() ) );
 
409
        setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); // no need for horizontal scrollbar with this
 
410
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
 
411
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
 
412
 
 
413
        setMinimumHeight(0);
 
414
 
 
415
        connect(this, SIGNAL(textChanged()), SLOT(recalculateSize()));
182
416
}
183
417
 
184
418
LineEdit::~LineEdit()
185
419
{
186
420
}
187
421
 
188
 
/*!
189
 
 * Returns true if the dialog could be automatically resized by LineEdit.
190
 
 */
191
 
bool LineEdit::allowResize() const
192
 
{
193
 
        QWidget *topParent = topLevelWidget();
194
 
 
195
 
        QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
196
 
        float desktopArea = desktop.width() * desktop.height();
197
 
        float dialogArea  = topParent->frameGeometry().width() * topParent->frameGeometry().height();
198
 
 
199
 
        // maximized and large chat windows shoulnd't resize the dialog
200
 
        if ( (dialogArea / desktopArea) > 0.9 )
201
 
                return false;
202
 
 
203
 
        return true;
204
 
}
205
 
 
206
 
/*!
207
 
 * In this implementation, it is quivalent to sizeHint().
208
 
 */
209
422
QSize LineEdit::minimumSizeHint() const
210
423
{
211
 
        return sizeHint();
 
424
        QSize sh = QTextEdit::minimumSizeHint();
 
425
        sh.setHeight(fontMetrics().height() + 1);
 
426
        sh += QSize(0, QFrame::lineWidth() * 2);
 
427
        return sh;
212
428
}
213
429
 
214
 
/*!
215
 
 * All magic is contained within this function. It determines the possible maximum
216
 
 * height, and controls the appearance of vertical scrollbar.
217
 
 */
218
430
QSize LineEdit::sizeHint() const
219
431
{
220
 
        if ( lastSize.width() != 0 && lastSize.height() != 0 )
221
 
                return lastSize;
222
 
 
223
 
        lastSize.setWidth( QTextEdit::sizeHint().width() );
224
 
        int h = 7;
225
 
 
226
 
        if ( paragraphs() > 0 ) {
227
 
                for ( int i = 0; i < paragraphs(); i++ ) {
228
 
                        h += paragraphRect( i ).height();
229
 
                }
230
 
        }
231
 
 
232
 
        QWidget *topParent = topLevelWidget();
233
 
        QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
234
 
        int dh = h - height();
235
 
 
236
 
        bool showScrollBar = false;
237
 
 
238
 
        // check that our dialog's height doesn't exceed the desktop's
239
 
        if ( allowResize() && (topParent->frameGeometry().height() + dh) >= desktop.height() ) {
240
 
                // handles the case when the dialog could be resized,
241
 
                // but lineedit wants to occupy too much space, so we should limit it
242
 
                h = desktop.height() - ( topParent->frameGeometry().height() - height() );
243
 
                showScrollBar = true;
244
 
        }
245
 
        else if ( !allowResize() && (h > topParent->geometry().height()/2) ) {
246
 
                // handles the case when the dialog could not be resized(i.e. it's maximized).
247
 
                // in this case we limit maximum height of lineedit to the half of dialog's
248
 
                // full height
249
 
                h = topParent->geometry().height() / 2;
250
 
                showScrollBar = true;
251
 
        }
252
 
 
253
 
        // enable vertical scrollbar only when we're surely in need for it
254
 
        QTextEdit *textEdit = (QTextEdit *)this;
255
 
        if ( showScrollBar )
256
 
                textEdit->setVScrollBarMode( AlwaysOn );
257
 
        else
258
 
                textEdit->setVScrollBarMode( AlwaysOff );
259
 
 
260
 
        lastSize.setHeight( h );
261
 
        return lastSize;
262
 
}
263
 
 
264
 
/*!
265
 
 * Handles automatic dialog resize.
266
 
 */
 
432
        QSize sh = QTextEdit::sizeHint();
 
433
        sh.setHeight(int(document()->documentLayout()->documentSize().height()));
 
434
        sh += QSize(0, QFrame::lineWidth() * 2);
 
435
        ((QTextEdit*)this)->setMaximumHeight(sh.height());
 
436
        return sh;
 
437
}
 
438
 
 
439
void LineEdit::resizeEvent(QResizeEvent* e)
 
440
{
 
441
        ChatEdit::resizeEvent(e);
 
442
        QTimer::singleShot(0, this, SLOT(updateScrollBar()));
 
443
}
 
444
 
267
445
void LineEdit::recalculateSize()
268
446
{
269
 
        if ( !isUpdatesEnabled() )
270
 
                return;
271
 
 
272
 
        QSize oldSize = lastSize;
273
 
        lastSize = QSize( 0, 0 ); // force sizeHint() to update
274
 
        QSize newSize = sizeHint();
275
 
 
276
 
        if ( QABS(newSize.height() - oldSize.height()) > 1 ) {
277
 
                if ( allowResize() ) {
278
 
                        parentWidget()->layout()->setEnabled( false ); // try to reduce some flicker
279
 
 
280
 
                        QWidget *topParent = topLevelWidget();
281
 
                        int dh = newSize.height() - oldSize.height();
282
 
 
283
 
                        // if we're going to shrink dialog considerably, minimum
284
 
                        // size will prevent us from doing it. Activating main
285
 
                        // layout after resize will reset minimum sizes to sensible values
286
 
                        topParent->setMinimumSize( 10, 10 );
287
 
 
288
 
                        topParent->resize( topParent->width(),
289
 
                                           topParent->height() + dh );
290
 
 
291
 
                        bool canMove = dh > 0;
292
 
                        int  newy    = topParent->y();
293
 
 
294
 
                        // try to move window to its old position
295
 
                        if ( movedWindow() && dh < 0 ) {
296
 
                                newy = initialWindowGeometry.y();
297
 
                                canMove = true;
298
 
                        }
299
 
 
300
 
                        // check, if we need to move dialog upper
301
 
                        QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
302
 
                        if ( canMove && ( newy + topParent->frameGeometry().height() >= desktop.bottom() ) ) {
303
 
                                // initialize default window position
304
 
                                if ( !movedWindow() ) {
305
 
                                        initialWindowGeometry = topParent->frameGeometry();
306
 
                                }
307
 
 
308
 
                                newy = QMAX(0, desktop.bottom() - topParent->frameGeometry().height());
309
 
                        }
310
 
 
311
 
                        if ( canMove && newy != topParent->y() ) {
312
 
                                topParent->move( topParent->x(), newy );
313
 
                                moveTo = topParent->pos();
314
 
                        }
315
 
 
316
 
                        parentWidget()->layout()->setEnabled( true );
317
 
                }
318
 
 
319
 
                // issue a layout update
320
 
                parentWidget()->layout()->activate();
321
 
        }
322
 
}
323
 
 
324
 
void LineEdit::resizeEvent( QResizeEvent *e )
325
 
{
326
 
        // issue a re-layout, just in case
327
 
        lastSize = QSize( 0, 0 ); // force sizeHint() to update
328
 
        sizeHint(); // update the size hint, and cache the value
329
 
        topLevelWidget()->layout()->activate();
330
 
 
331
 
        PsiTextView::resizeEvent( e );
332
 
}
333
 
 
334
 
void LineEdit::setUpdatesEnabled( bool enable )
335
 
{
336
 
        bool ue = isUpdatesEnabled();
337
 
        ChatEdit::setUpdatesEnabled( enable );
338
 
 
339
 
        if ( !ue && enable )
340
 
                recalculateSize();
341
 
}
342
 
 
343
 
bool LineEdit::eventFilter(QObject *watched, QEvent *e)
344
 
{
345
 
        if ( !parentWidget()->layout()->isEnabled() )
346
 
                return ChatEdit::eventFilter( watched, e );
347
 
 
348
 
        if ( e->type() == QEvent::Reparent ) {
349
 
                // In case of tabbed chats, dialog could be reparented to a higher-level dialog
350
 
                // we need to get move events from it too. And unnecessary event filters
351
 
                // are automatically cleaned up by Qt.
352
 
                topLevelWidget()->installEventFilter( this );
353
 
        }
354
 
        else if ( e->type() == QEvent::Move ) {
355
 
                QWidget *topParent = topLevelWidget();
356
 
                if ( watched == topParent ) {
357
 
                        moveTimer->start( 100, true );
358
 
                }
359
 
        }
360
 
 
361
 
        return ChatEdit::eventFilter( watched, e );
362
 
}
363
 
 
364
 
//! This function serves as a workaround for multiple move events, some of which
365
 
//! have incorrect coordinates (at least on KDE)
366
 
void LineEdit::checkMoved()
367
 
{
368
 
        QWidget *topParent = topLevelWidget();
369
 
        if ( QABS(moveTo.x() - topParent->x()) > 1 ||
370
 
             QABS(moveTo.y() - topParent->y()) > 1 ) {
371
 
                moveTo = topParent->pos();
372
 
                initialWindowGeometry = QRect( 0, 0, 0, 0 );
373
 
        }
374
 
}
375
 
 
376
 
bool LineEdit::movedWindow() const
377
 
{
378
 
        return initialWindowGeometry.left()  ||
379
 
               initialWindowGeometry.top()   ||
380
 
               initialWindowGeometry.width() ||
381
 
               initialWindowGeometry.height();
382
 
}
383
 
 
 
447
        updateGeometry();
 
448
        QTimer::singleShot(0, this, SLOT(updateScrollBar()));
 
449
}
 
450
 
 
451
void LineEdit::updateScrollBar()
 
452
{
 
453
        setVerticalScrollBarPolicy(sizeHint().height() > height() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
 
454
        ensureCursorVisible();
 
455
}