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 | //----------------------------------------------------------------------------
|
---|
31 | ChatView::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 |
|
---|
48 | ChatView::~ChatView()
|
---|
49 | {
|
---|
50 | }
|
---|
51 |
|
---|
52 | bool ChatView::focusNextPrevChild(bool next)
|
---|
53 | {
|
---|
54 | return QWidget::focusNextPrevChild(next);
|
---|
55 | }
|
---|
56 |
|
---|
57 | void 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 |
|
---|
79 | void 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 |
|
---|
100 | void ChatView::autoCopy(bool copyAvailable)
|
---|
101 | {
|
---|
102 | if ( isReadOnly() && copyAvailable && option.autoCopy ) {
|
---|
103 | copy();
|
---|
104 | }
|
---|
105 | }
|
---|
106 |
|
---|
107 | //----------------------------------------------------------------------------
|
---|
108 | // ChatEdit
|
---|
109 | //----------------------------------------------------------------------------
|
---|
110 | ChatEdit::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 |
|
---|
122 | ChatEdit::~ChatEdit()
|
---|
123 | {
|
---|
124 | }
|
---|
125 |
|
---|
126 | bool ChatEdit::focusNextPrevChild(bool next)
|
---|
127 | {
|
---|
128 | return QWidget::focusNextPrevChild(next);
|
---|
129 | }
|
---|
130 |
|
---|
131 | void 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 | //----------------------------------------------------------------------------
|
---|
158 | LineEdit::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 |
|
---|
182 | LineEdit::~LineEdit()
|
---|
183 | {
|
---|
184 | }
|
---|
185 |
|
---|
186 | /*!
|
---|
187 | * Returns true if the dialog could be automatically resized by LineEdit.
|
---|
188 | */
|
---|
189 | bool 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 | */
|
---|
207 | QSize 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 | */
|
---|
216 | QSize 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 | */
|
---|
265 | void 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 |
|
---|
322 | void 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 |
|
---|
332 | void LineEdit::setUpdatesEnabled( bool enable )
|
---|
333 | {
|
---|
334 | bool ue = isUpdatesEnabled();
|
---|
335 | ChatEdit::setUpdatesEnabled( enable );
|
---|
336 |
|
---|
337 | if ( !ue && enable )
|
---|
338 | recalculateSize();
|
---|
339 | }
|
---|
340 |
|
---|
341 | bool 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)
|
---|
364 | void 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 |
|
---|
374 | bool LineEdit::movedWindow() const
|
---|
375 | {
|
---|
376 | return initialWindowGeometry.left() ||
|
---|
377 | initialWindowGeometry.top() ||
|
---|
378 | initialWindowGeometry.width() ||
|
---|
379 | initialWindowGeometry.height();
|
---|
380 | }
|
---|
381 |
|
---|