source: psi/trunk/src/msgmle.cpp@ 10

Last change on this file since 10 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 10.7 KB
Line 
1/*
2 * msgmle.cpp - subclass of PsiTextView to handle various hotkeys
3 * Copyright (C) 2001-2003 Justin Karneges, Michail Pishchagin
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include<qapplication.h>
22#include<qlayout.h>
23#include<qtimer.h>
24
25#include"common.h"
26#include"msgmle.h"
27
28//----------------------------------------------------------------------------
29// ChatView
30//----------------------------------------------------------------------------
31ChatView::ChatView(QWidget *parent, const char *name)
32: PsiTextView(parent, name)
33{
34 setWordWrap(WidgetWidth);
35 setWrapPolicy(AtWordOrDocumentBoundary);
36
37 setTextFormat(RichText);
38 setReadOnly(true);
39 setUndoRedoEnabled(false);
40 setHScrollBarMode(QScrollView::AlwaysOff);
41
42#ifndef Q_WS_X11 // linux has this feature built-in
43 connect(this, SIGNAL(copyAvailable(bool)), SLOT(autoCopy(bool)));
44#endif
45
46}
47
48ChatView::~ChatView()
49{
50}
51
52bool ChatView::focusNextPrevChild(bool next)
53{
54 return QWidget::focusNextPrevChild(next);
55}
56
57void ChatView::keyPressEvent(QKeyEvent *e)
58{
59 if(e->key() == Key_Escape)
60 e->ignore();
61#ifdef Q_WS_MAC
62 else if(e->key() == Key_W && e->state() & ControlButton)
63 e->ignore();
64#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
72 insert("\n");
73 else if(e->key() == Key_U && (e->state() & ControlButton))
74 setText("");
75 else
76 QTextEdit::keyPressEvent(e);
77}
78
79void ChatView::resizeEvent(QResizeEvent *e)
80{
81 // This fixes flyspray #45
82 if(contentsY() == contentsHeight() - visibleHeight())
83 scrollToBottom();
84
85 QTextEdit::resizeEvent(e);
86}
87
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
100void ChatView::autoCopy(bool copyAvailable)
101{
102 if ( isReadOnly() && copyAvailable && option.autoCopy ) {
103 copy();
104 }
105}
106
107//----------------------------------------------------------------------------
108// ChatEdit
109//----------------------------------------------------------------------------
110ChatEdit::ChatEdit(QWidget *parent, const char *name)
111: PsiTextView(parent, name)
112{
113 setWordWrap(QTextEdit::WidgetWidth);
114
115 setReadOnly(false);
116 setUndoRedoEnabled(true);
117
118 setTextFormat(PlainText);
119 setMinimumHeight(48);
120}
121
122ChatEdit::~ChatEdit()
123{
124}
125
126bool ChatEdit::focusNextPrevChild(bool next)
127{
128 return QWidget::focusNextPrevChild(next);
129}
130
131void ChatEdit::keyPressEvent(QKeyEvent *e)
132{
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
138 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))
144 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
152 QTextEdit::keyPressEvent(e);
153}
154
155//----------------------------------------------------------------------------
156// LineEdit
157//----------------------------------------------------------------------------
158LineEdit::LineEdit( QWidget *parent, const char *name )
159 : ChatEdit( parent, name )
160{
161 lastSize = QSize( 0, 0 );
162 initialWindowGeometry = QRect( 0, 0, 0, 0 );
163
164 QWidget *topParent = topLevelWidget();
165 topParent->installEventFilter( this );
166 moveTo = QPoint(topParent->x(), topParent->y());
167
168 moveTimer = new QTimer( this );
169 connect( moveTimer, SIGNAL(timeout()), SLOT(checkMoved()) );
170
171 // LineEdit's size hint is to be vertically as small as possible
172 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum );
173
174 setWrapPolicy( AtWordOrDocumentBoundary ); // no need for horizontal scrollbar with this
175 setHScrollBarMode(QScrollView::AlwaysOff);
176
177 setMinimumHeight(-1);
178
179 connect( this, SIGNAL( textChanged() ), SLOT( recalculateSize() ) );
180}
181
182LineEdit::~LineEdit()
183{
184}
185
186/*!
187 * Returns true if the dialog could be automatically resized by LineEdit.
188 */
189bool LineEdit::allowResize() const
190{
191 QWidget *topParent = topLevelWidget();
192
193 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
194 float desktopArea = desktop.width() * desktop.height();
195 float dialogArea = topParent->frameGeometry().width() * topParent->frameGeometry().height();
196
197 // maximized and large chat windows shoulnd't resize the dialog
198 if ( (dialogArea / desktopArea) > 0.9 )
199 return false;
200
201 return true;
202}
203
204/*!
205 * In this implementation, it is quivalent to sizeHint().
206 */
207QSize LineEdit::minimumSizeHint() const
208{
209 return sizeHint();
210}
211
212/*!
213 * All magic is contained within this function. It determines the possible maximum
214 * height, and controls the appearance of vertical scrollbar.
215 */
216QSize LineEdit::sizeHint() const
217{
218 if ( lastSize.width() != 0 && lastSize.height() != 0 )
219 return lastSize;
220
221 lastSize.setWidth( QTextEdit::sizeHint().width() );
222 int h = 7;
223
224 if ( paragraphs() > 0 ) {
225 for ( int i = 0; i < paragraphs(); i++ ) {
226 h += paragraphRect( i ).height();
227 }
228 }
229
230 QWidget *topParent = topLevelWidget();
231 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
232 int dh = h - height();
233
234 bool showScrollBar = false;
235
236 // check that our dialog's height doesn't exceed the desktop's
237 if ( allowResize() && (topParent->frameGeometry().height() + dh) >= desktop.height() ) {
238 // handles the case when the dialog could be resized,
239 // but lineedit wants to occupy too much space, so we should limit it
240 h = desktop.height() - ( topParent->frameGeometry().height() - height() );
241 showScrollBar = true;
242 }
243 else if ( !allowResize() && (h > topParent->geometry().height()/2) ) {
244 // handles the case when the dialog could not be resized(i.e. it's maximized).
245 // in this case we limit maximum height of lineedit to the half of dialog's
246 // full height
247 h = topParent->geometry().height() / 2;
248 showScrollBar = true;
249 }
250
251 // enable vertical scrollbar only when we're surely in need for it
252 QTextEdit *textEdit = (QTextEdit *)this;
253 if ( showScrollBar )
254 textEdit->setVScrollBarMode( AlwaysOn );
255 else
256 textEdit->setVScrollBarMode( AlwaysOff );
257
258 lastSize.setHeight( h );
259 return lastSize;
260}
261
262/*!
263 * Handles automatic dialog resize.
264 */
265void LineEdit::recalculateSize()
266{
267 if ( !isUpdatesEnabled() )
268 return;
269
270 QSize oldSize = lastSize;
271 lastSize = QSize( 0, 0 ); // force sizeHint() to update
272 QSize newSize = sizeHint();
273
274 if ( QABS(newSize.height() - oldSize.height()) > 1 ) {
275 if ( allowResize() ) {
276 parentWidget()->layout()->setEnabled( false ); // try to reduce some flicker
277
278 QWidget *topParent = topLevelWidget();
279 int dh = newSize.height() - oldSize.height();
280
281 // if we're going to shrink dialog considerably, minimum
282 // size will prevent us from doing it. Activating main
283 // layout after resize will reset minimum sizes to sensible values
284 topParent->setMinimumSize( 10, 10 );
285
286 topParent->resize( topParent->width(),
287 topParent->height() + dh );
288
289 bool canMove = dh > 0;
290 int newy = topParent->y();
291
292 // try to move window to its old position
293 if ( movedWindow() && dh < 0 ) {
294 newy = initialWindowGeometry.y();
295 canMove = true;
296 }
297
298 // check, if we need to move dialog upper
299 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
300 if ( canMove && ( newy + topParent->frameGeometry().height() >= desktop.bottom() ) ) {
301 // initialize default window position
302 if ( !movedWindow() ) {
303 initialWindowGeometry = topParent->frameGeometry();
304 }
305
306 newy = QMAX(0, desktop.bottom() - topParent->frameGeometry().height());
307 }
308
309 if ( canMove && newy != topParent->y() ) {
310 topParent->move( topParent->x(), newy );
311 moveTo = topParent->pos();
312 }
313
314 parentWidget()->layout()->setEnabled( true );
315 }
316
317 // issue a layout update
318 parentWidget()->layout()->activate();
319 }
320}
321
322void LineEdit::resizeEvent( QResizeEvent *e )
323{
324 // issue a re-layout, just in case
325 lastSize = QSize( 0, 0 ); // force sizeHint() to update
326 sizeHint(); // update the size hint, and cache the value
327 topLevelWidget()->layout()->activate();
328
329 PsiTextView::resizeEvent( e );
330}
331
332void LineEdit::setUpdatesEnabled( bool enable )
333{
334 bool ue = isUpdatesEnabled();
335 ChatEdit::setUpdatesEnabled( enable );
336
337 if ( !ue && enable )
338 recalculateSize();
339}
340
341bool LineEdit::eventFilter(QObject *watched, QEvent *e)
342{
343 if ( !parentWidget()->layout()->isEnabled() )
344 return ChatEdit::eventFilter( watched, e );
345
346 if ( e->type() == QEvent::Reparent ) {
347 // In case of tabbed chats, dialog could be reparented to a higher-level dialog
348 // we need to get move events from it too. And unnecessary event filters
349 // are automatically cleaned up by Qt.
350 topLevelWidget()->installEventFilter( this );
351 }
352 else if ( e->type() == QEvent::Move ) {
353 QWidget *topParent = topLevelWidget();
354 if ( watched == topParent ) {
355 moveTimer->start( 100, true );
356 }
357 }
358
359 return ChatEdit::eventFilter( watched, e );
360}
361
362//! This function serves as a workaround for multiple move events, some of which
363//! have incorrect coordinates (at least on KDE)
364void LineEdit::checkMoved()
365{
366 QWidget *topParent = topLevelWidget();
367 if ( QABS(moveTo.x() - topParent->x()) > 1 ||
368 QABS(moveTo.y() - topParent->y()) > 1 ) {
369 moveTo = topParent->pos();
370 initialWindowGeometry = QRect( 0, 0, 0, 0 );
371 }
372}
373
374bool LineEdit::movedWindow() const
375{
376 return initialWindowGeometry.left() ||
377 initialWindowGeometry.top() ||
378 initialWindowGeometry.width() ||
379 initialWindowGeometry.height();
380}
381
Note: See TracBrowser for help on using the repository browser.