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

Last change on this file since 63 was 18, checked in by dmik, 19 years ago

Chat: Added popular Ctrl+Tab and Ctrl+Shift+Tab as hotkeys to switch between tabs in the Tabbed chat mode.

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
126void ChatEdit::keyPressEvent(QKeyEvent *e)
127{
128 if(e->key() == Key_Escape || (e->key() == Key_W && e->state() & ControlButton))
129 e->ignore();
130 else if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
131 e->ignore();
132 else if(e->key() == Key_M && (e->state() & ControlButton)) // newline
133 insert("\n");
134 else if(e->key() == Key_H && (e->state() & ControlButton)) // history
135 e->ignore();
136 else if(e->key() == Key_S && (e->state() & AltButton))
137 e->ignore();
138 else if(e->key() == Key_Backtab && (e->state() & ControlButton))
139 e->ignore();
140 else if(e->key() == Key_Tab && (e->state() & ControlButton))
141 e->ignore();
142 else if(e->key() == Key_U && (e->state() & ControlButton))
143 setText("");
144 else if((e->key() == Key_Return || e->key() == Key_Enter) && !(e->state() & ShiftButton) && option.chatSoftReturn)
145 e->ignore();
146 else if((e->key() == Key_PageUp || e->key() == Key_PageDown) && (e->state() & ShiftButton))
147 e->ignore();
148 else if((e->key() == Key_PageUp || e->key() == Key_PageDown) && (e->state() & ControlButton))
149 e->ignore();
150 else
151 QTextEdit::keyPressEvent(e);
152}
153
154//----------------------------------------------------------------------------
155// LineEdit
156//----------------------------------------------------------------------------
157LineEdit::LineEdit( QWidget *parent, const char *name )
158 : ChatEdit( parent, name )
159{
160 lastSize = QSize( 0, 0 );
161 initialWindowGeometry = QRect( 0, 0, 0, 0 );
162
163 QWidget *topParent = topLevelWidget();
164 topParent->installEventFilter( this );
165 moveTo = QPoint(topParent->x(), topParent->y());
166
167 moveTimer = new QTimer( this );
168 connect( moveTimer, SIGNAL(timeout()), SLOT(checkMoved()) );
169
170 // LineEdit's size hint is to be vertically as small as possible
171 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum );
172
173 setWrapPolicy( AtWordOrDocumentBoundary ); // no need for horizontal scrollbar with this
174 setHScrollBarMode(QScrollView::AlwaysOff);
175
176 setMinimumHeight(-1);
177
178 connect( this, SIGNAL( textChanged() ), SLOT( recalculateSize() ) );
179}
180
181LineEdit::~LineEdit()
182{
183}
184
185/*!
186 * Returns true if the dialog could be automatically resized by LineEdit.
187 */
188bool LineEdit::allowResize() const
189{
190 QWidget *topParent = topLevelWidget();
191
192 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
193 float desktopArea = desktop.width() * desktop.height();
194 float dialogArea = topParent->frameGeometry().width() * topParent->frameGeometry().height();
195
196 // maximized and large chat windows shoulnd't resize the dialog
197 if ( (dialogArea / desktopArea) > 0.9 )
198 return false;
199
200 return true;
201}
202
203/*!
204 * In this implementation, it is quivalent to sizeHint().
205 */
206QSize LineEdit::minimumSizeHint() const
207{
208 return sizeHint();
209}
210
211/*!
212 * All magic is contained within this function. It determines the possible maximum
213 * height, and controls the appearance of vertical scrollbar.
214 */
215QSize LineEdit::sizeHint() const
216{
217 if ( lastSize.width() != 0 && lastSize.height() != 0 )
218 return lastSize;
219
220 lastSize.setWidth( QTextEdit::sizeHint().width() );
221 int h = 7;
222
223 if ( paragraphs() > 0 ) {
224 for ( int i = 0; i < paragraphs(); i++ ) {
225 h += paragraphRect( i ).height();
226 }
227 }
228
229 QWidget *topParent = topLevelWidget();
230 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
231 int dh = h - height();
232
233 bool showScrollBar = false;
234
235 // check that our dialog's height doesn't exceed the desktop's
236 if ( allowResize() && (topParent->frameGeometry().height() + dh) >= desktop.height() ) {
237 // handles the case when the dialog could be resized,
238 // but lineedit wants to occupy too much space, so we should limit it
239 h = desktop.height() - ( topParent->frameGeometry().height() - height() );
240 showScrollBar = true;
241 }
242 else if ( !allowResize() && (h > topParent->geometry().height()/2) ) {
243 // handles the case when the dialog could not be resized(i.e. it's maximized).
244 // in this case we limit maximum height of lineedit to the half of dialog's
245 // full height
246 h = topParent->geometry().height() / 2;
247 showScrollBar = true;
248 }
249
250 // enable vertical scrollbar only when we're surely in need for it
251 QTextEdit *textEdit = (QTextEdit *)this;
252 if ( showScrollBar )
253 textEdit->setVScrollBarMode( AlwaysOn );
254 else
255 textEdit->setVScrollBarMode( AlwaysOff );
256
257 lastSize.setHeight( h );
258 return lastSize;
259}
260
261/*!
262 * Handles automatic dialog resize.
263 */
264void LineEdit::recalculateSize()
265{
266 if ( !isUpdatesEnabled() )
267 return;
268
269 QSize oldSize = lastSize;
270 lastSize = QSize( 0, 0 ); // force sizeHint() to update
271 QSize newSize = sizeHint();
272
273 if ( QABS(newSize.height() - oldSize.height()) > 1 ) {
274 if ( allowResize() ) {
275 parentWidget()->layout()->setEnabled( false ); // try to reduce some flicker
276
277 QWidget *topParent = topLevelWidget();
278 int dh = newSize.height() - oldSize.height();
279
280 // if we're going to shrink dialog considerably, minimum
281 // size will prevent us from doing it. Activating main
282 // layout after resize will reset minimum sizes to sensible values
283 topParent->setMinimumSize( 10, 10 );
284
285 topParent->resize( topParent->width(),
286 topParent->height() + dh );
287
288 bool canMove = dh > 0;
289 int newy = topParent->y();
290
291 // try to move window to its old position
292 if ( movedWindow() && dh < 0 ) {
293 newy = initialWindowGeometry.y();
294 canMove = true;
295 }
296
297 // check, if we need to move dialog upper
298 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
299 if ( canMove && ( newy + topParent->frameGeometry().height() >= desktop.bottom() ) ) {
300 // initialize default window position
301 if ( !movedWindow() ) {
302 initialWindowGeometry = topParent->frameGeometry();
303 }
304
305 newy = QMAX(0, desktop.bottom() - topParent->frameGeometry().height());
306 }
307
308 if ( canMove && newy != topParent->y() ) {
309 topParent->move( topParent->x(), newy );
310 moveTo = topParent->pos();
311 }
312
313 parentWidget()->layout()->setEnabled( true );
314 }
315
316 // issue a layout update
317 parentWidget()->layout()->activate();
318 }
319}
320
321void LineEdit::resizeEvent( QResizeEvent *e )
322{
323 // issue a re-layout, just in case
324 lastSize = QSize( 0, 0 ); // force sizeHint() to update
325 sizeHint(); // update the size hint, and cache the value
326 topLevelWidget()->layout()->activate();
327
328 PsiTextView::resizeEvent( e );
329}
330
331void LineEdit::setUpdatesEnabled( bool enable )
332{
333 bool ue = isUpdatesEnabled();
334 ChatEdit::setUpdatesEnabled( enable );
335
336 if ( !ue && enable )
337 recalculateSize();
338}
339
340bool LineEdit::eventFilter(QObject *watched, QEvent *e)
341{
342 if ( !parentWidget()->layout()->isEnabled() )
343 return ChatEdit::eventFilter( watched, e );
344
345 if ( e->type() == QEvent::Reparent ) {
346 // In case of tabbed chats, dialog could be reparented to a higher-level dialog
347 // we need to get move events from it too. And unnecessary event filters
348 // are automatically cleaned up by Qt.
349 topLevelWidget()->installEventFilter( this );
350 }
351 else if ( e->type() == QEvent::Move ) {
352 QWidget *topParent = topLevelWidget();
353 if ( watched == topParent ) {
354 moveTimer->start( 100, true );
355 }
356 }
357
358 return ChatEdit::eventFilter( watched, e );
359}
360
361//! This function serves as a workaround for multiple move events, some of which
362//! have incorrect coordinates (at least on KDE)
363void LineEdit::checkMoved()
364{
365 QWidget *topParent = topLevelWidget();
366 if ( QABS(moveTo.x() - topParent->x()) > 1 ||
367 QABS(moveTo.y() - topParent->y()) > 1 ) {
368 moveTo = topParent->pos();
369 initialWindowGeometry = QRect( 0, 0, 0, 0 );
370 }
371}
372
373bool LineEdit::movedWindow() const
374{
375 return initialWindowGeometry.left() ||
376 initialWindowGeometry.top() ||
377 initialWindowGeometry.width() ||
378 initialWindowGeometry.height();
379}
380
Note: See TracBrowser for help on using the repository browser.