Diffstat (limited to 'noncore/multimedia/showimg/showimg.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/multimedia/showimg/showimg.cpp | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/noncore/multimedia/showimg/showimg.cpp b/noncore/multimedia/showimg/showimg.cpp new file mode 100644 index 0000000..c56994d --- a/dev/null +++ b/noncore/multimedia/showimg/showimg.cpp | |||
@@ -0,0 +1,557 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qtopia Environment. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | // | ||
22 | // Full-screen and rotation options contributed by Robert Wittams <robert@wittams.com> | ||
23 | // | ||
24 | |||
25 | #include "showimg.h" | ||
26 | |||
27 | #include <qpe/resource.h> | ||
28 | #include <qpe/fileselector.h> | ||
29 | #include <qpe/applnk.h> | ||
30 | |||
31 | #include <qpe/qpemenubar.h> | ||
32 | #include <qwidgetstack.h> | ||
33 | #include <qpe/qpetoolbar.h> | ||
34 | #include <qaction.h> | ||
35 | #include <qfiledialog.h> | ||
36 | #include <qmessagebox.h> | ||
37 | #include <qpopupmenu.h> | ||
38 | #include <qlabel.h> | ||
39 | #include <qpainter.h> | ||
40 | #include <qkeycode.h> | ||
41 | #include <qapplication.h> | ||
42 | #include <qclipboard.h> | ||
43 | #include <qtimer.h> | ||
44 | |||
45 | |||
46 | ImagePane::ImagePane( QWidget *parent=0 ) : QWidget( parent ) | ||
47 | { | ||
48 | vb = new QVBoxLayout( this ); | ||
49 | |||
50 | image = new ImageWidget( this ); | ||
51 | connect(image, SIGNAL( clicked() ), this, SLOT( imageClicked() )); | ||
52 | |||
53 | vb->addWidget( image ); | ||
54 | |||
55 | status = new QLabel( this ); | ||
56 | status->setFixedHeight( fontMetrics().height() + 4 ); | ||
57 | vb->addWidget( status ); | ||
58 | } | ||
59 | |||
60 | void ImagePane::setPixmap( const QPixmap &pm ) | ||
61 | { | ||
62 | image->setPixmap( pm ); | ||
63 | image->repaint( false ); | ||
64 | } | ||
65 | |||
66 | void ImagePane::imageClicked() | ||
67 | { | ||
68 | emit clicked(); | ||
69 | } | ||
70 | |||
71 | void ImagePane::showStatus() | ||
72 | { | ||
73 | delete vb; | ||
74 | vb = new QVBoxLayout( this ); | ||
75 | vb->addWidget( image ); | ||
76 | status->show(); | ||
77 | vb->addWidget( status ); | ||
78 | } | ||
79 | |||
80 | |||
81 | void ImagePane::hideStatus() | ||
82 | { | ||
83 | delete vb; | ||
84 | vb = new QVBoxLayout( this ); | ||
85 | vb->addWidget( image ); | ||
86 | status->hide(); | ||
87 | } | ||
88 | |||
89 | //=========================================================================== | ||
90 | /* | ||
91 | Draws the portion of the scaled pixmap that needs to be updated | ||
92 | */ | ||
93 | |||
94 | void ImageWidget::paintEvent( QPaintEvent *e ) | ||
95 | { | ||
96 | QPainter painter(this); | ||
97 | |||
98 | painter.setClipRect(e->rect()); | ||
99 | painter.setBrush( black ); | ||
100 | painter.drawRect( 0, 0, width(), height() ); | ||
101 | |||
102 | if ( pixmap.size() != QSize( 0, 0 ) ) { // is an image loaded? | ||
103 | painter.drawPixmap((width() - pixmap.width()) / 2, (height() - pixmap.height()) / 2, pixmap); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | void ImageWidget::mouseReleaseEvent(QMouseEvent *) | ||
108 | { | ||
109 | emit clicked(); | ||
110 | } | ||
111 | |||
112 | |||
113 | //=========================================================================== | ||
114 | |||
115 | ImageViewer::ImageViewer( QWidget *parent, const char *name, int wFlags ) | ||
116 | : QMainWindow( parent, name, wFlags ), filename( 0 ), | ||
117 | pickx( -1 ), picky( -1 ), clickx( -1 ), clicky( -1 ), bFromDocView( FALSE ) | ||
118 | { | ||
119 | setCaption( tr("Image Viewer") ); | ||
120 | setIcon( Resource::loadPixmap( "ImageViewer" ) ); | ||
121 | |||
122 | isFullScreen = FALSE; | ||
123 | |||
124 | setToolBarsMovable( FALSE ); | ||
125 | |||
126 | toolBar = new QPEToolBar( this ); | ||
127 | toolBar->setHorizontalStretchable( TRUE ); | ||
128 | |||
129 | menubar = new QPEMenuBar( toolBar ); | ||
130 | |||
131 | QStrList fmt = QImage::outputFormats(); | ||
132 | |||
133 | QPopupMenu *edit = new QPopupMenu( menubar ); | ||
134 | QPopupMenu *view = new QPopupMenu( menubar ); | ||
135 | |||
136 | menubar->insertItem( "Edit", edit ); | ||
137 | menubar->insertItem( "View", view ); | ||
138 | |||
139 | edit->insertItem(tr("Horizontal flip"), this, SLOT(hFlip()), 0); | ||
140 | edit->insertItem(tr("Vertical flip"), this, SLOT(vFlip()), 0); | ||
141 | |||
142 | stack = new QWidgetStack( this ); | ||
143 | stack->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); | ||
144 | setCentralWidget( stack ); | ||
145 | |||
146 | imagePanel = new ImagePane( stack ); | ||
147 | connect(imagePanel, SIGNAL(clicked()), this, SLOT(normalView())); | ||
148 | |||
149 | fileSelector = new FileSelector("image/*", stack, "fs"); | ||
150 | fileSelector->setNewVisible(FALSE); | ||
151 | fileSelector->setCloseVisible(FALSE); | ||
152 | connect( fileSelector, SIGNAL( closeMe() ), this, SLOT( closeFileSelector() ) ); | ||
153 | connect( fileSelector, SIGNAL( fileSelected( const DocLnk &) ), this, SLOT( openFile( const DocLnk & ) ) ); | ||
154 | |||
155 | toolBar = new QPEToolBar( this ); | ||
156 | |||
157 | QAction *a; | ||
158 | |||
159 | a = new QAction( tr( "Open" ), Resource::loadPixmap( "fileopen" ), QString::null, 0, this, 0 ); | ||
160 | connect( a, SIGNAL( activated() ), this, SLOT( open() ) ); | ||
161 | a->addTo( toolBar ); | ||
162 | |||
163 | a = new QAction( tr( "Rotate 180" ), Resource::loadPixmap( "repeat" ), QString::null, 0, this, 0 ); | ||
164 | connect( a, SIGNAL( activated() ), this, SLOT( rot180() ) ); | ||
165 | a->addTo( toolBar ); | ||
166 | a->addTo( edit ); | ||
167 | |||
168 | a = new QAction( tr( "Rotate 90"), Resource::loadPixmap( "rotate90" ), QString::null, 0, this, 0); | ||
169 | connect( a, SIGNAL( activated() ), this, SLOT( rot90() ) ); | ||
170 | a->addTo( toolBar ); | ||
171 | a->addTo( edit ); | ||
172 | |||
173 | a = new QAction( tr( "Fullscreen" ), Resource::loadPixmap( "fullscreen" ), QString::null, 0, this, 0 ); | ||
174 | connect( a, SIGNAL( activated() ), this, SLOT( fullScreen() ) ); | ||
175 | a->addTo( toolBar ); | ||
176 | a->addTo( view); | ||
177 | |||
178 | stack->raiseWidget( fileSelector ); | ||
179 | |||
180 | setMouseTracking( TRUE ); | ||
181 | } | ||
182 | |||
183 | ImageViewer::~ImageViewer() | ||
184 | { | ||
185 | delete imagePanel; // in case it is fullscreen | ||
186 | } | ||
187 | |||
188 | void ImageViewer::setDocument(const QString& fileref) | ||
189 | { | ||
190 | delayLoad = fileref; | ||
191 | stack->raiseWidget(imagePanel); | ||
192 | QTimer::singleShot( 0, this, SLOT(doDelayedLoad()) ); | ||
193 | } | ||
194 | |||
195 | void ImageViewer::doDelayedLoad() | ||
196 | { | ||
197 | show(delayLoad); | ||
198 | } | ||
199 | |||
200 | void ImageViewer::show() | ||
201 | { | ||
202 | normalView(); | ||
203 | QMainWindow::show(); | ||
204 | } | ||
205 | |||
206 | void ImageViewer::show(const QString& fileref) | ||
207 | { | ||
208 | bFromDocView = TRUE; | ||
209 | closeFileSelector(); | ||
210 | DocLnk link(fileref); | ||
211 | if ( link.isValid() ) { | ||
212 | openFile(link); | ||
213 | } else { | ||
214 | filename = fileref; | ||
215 | updateCaption( fileref ); | ||
216 | loadImage( fileref ); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | void ImageViewer::openFile( const DocLnk &file ) | ||
221 | { | ||
222 | closeFileSelector(); | ||
223 | DocLnk link(file); | ||
224 | updateCaption( link.name() ); | ||
225 | loadImage( link.file() ); | ||
226 | } | ||
227 | |||
228 | void ImageViewer::open() | ||
229 | { | ||
230 | stack->raiseWidget(fileSelector); | ||
231 | } | ||
232 | |||
233 | void ImageViewer::closeFileSelector() | ||
234 | { | ||
235 | stack->raiseWidget(imagePanel); | ||
236 | } | ||
237 | |||
238 | void ImageViewer::updateCaption( QString name ) | ||
239 | { | ||
240 | int sep = name.findRev( '/' ); | ||
241 | if ( sep >= 0 ) | ||
242 | name = name.mid( sep+1 ); | ||
243 | setCaption( name + tr(" - Image Viewer") ); | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | This function loads an image from a file. | ||
248 | */ | ||
249 | |||
250 | void ImageViewer::loadImage( const char *fileName ) | ||
251 | { | ||
252 | filename = fileName; | ||
253 | if ( filename ) { | ||
254 | QApplication::setOverrideCursor( waitCursor ); // this might take time | ||
255 | imagePanel->statusLabel()->setText( tr("Loading image...") ); | ||
256 | qApp->processEvents(); | ||
257 | bool ok = image.load(filename, 0); | ||
258 | pickx = -1; | ||
259 | clickx = -1; | ||
260 | if ( ok ) | ||
261 | ok = reconvertImage(); | ||
262 | if ( !ok ) { | ||
263 | pm.resize(0,0); // couldn't load image | ||
264 | update(); | ||
265 | } | ||
266 | QApplication::restoreOverrideCursor();// restore original cursor | ||
267 | } | ||
268 | updateStatus(); | ||
269 | imagePanel->setPixmap( pmScaled ); | ||
270 | stack->raiseWidget(imagePanel); | ||
271 | } | ||
272 | |||
273 | bool ImageViewer::loadSelected() | ||
274 | { | ||
275 | bool ok = false; | ||
276 | if ( stack->visibleWidget() == fileSelector ) { | ||
277 | const DocLnk *link = fileSelector->selected(); | ||
278 | if ( link ) { | ||
279 | if ( link->file() != filename ) { | ||
280 | updateCaption( link->name() ); | ||
281 | filename = link->file(); | ||
282 | imagePanel->statusLabel()->setText( tr("Loading image...") ); | ||
283 | qApp->processEvents(); | ||
284 | ok = image.load(filename, 0); | ||
285 | if ( ok ) | ||
286 | ok = reconvertImage(); | ||
287 | if ( !ok ) | ||
288 | pm.resize(0,0); | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | if ( !image.isNull() ) { | ||
293 | ok = true; | ||
294 | closeFileSelector(); | ||
295 | } | ||
296 | |||
297 | return ok; | ||
298 | } | ||
299 | |||
300 | bool ImageViewer::reconvertImage() | ||
301 | { | ||
302 | bool success = FALSE; | ||
303 | |||
304 | if ( image.isNull() ) return FALSE; | ||
305 | |||
306 | QApplication::setOverrideCursor( waitCursor ); // this might take time | ||
307 | if ( pm.convertFromImage(image /*, conversion_flags */ ) ) | ||
308 | { | ||
309 | pmScaled = QPixmap(); | ||
310 | scale(); | ||
311 | success = TRUE; // load successful | ||
312 | } else { | ||
313 | pm.resize(0,0); // couldn't load image | ||
314 | } | ||
315 | QApplication::restoreOverrideCursor();// restore original cursor | ||
316 | |||
317 | return success; // TRUE if loaded OK | ||
318 | } | ||
319 | |||
320 | |||
321 | int ImageViewer::calcHeight() | ||
322 | { | ||
323 | if ( !isFullScreen) | ||
324 | return height() - menubar->heightForWidth( width() ) | ||
325 | - imagePanel->statusLabel()->height(); | ||
326 | else | ||
327 | return qApp->desktop()->height(); | ||
328 | } | ||
329 | /* | ||
330 | This functions scales the pixmap in the member variable "pm" to fit the | ||
331 | widget size and puts the resulting pixmap in the member variable "pmScaled". | ||
332 | */ | ||
333 | |||
334 | void ImageViewer::scale() | ||
335 | { | ||
336 | int h = calcHeight(); | ||
337 | if ( image.isNull() ) return; | ||
338 | |||
339 | QApplication::setOverrideCursor( waitCursor ); // this might take time | ||
340 | if ( width() == pm.width() && h == pm.height() ) { // no need to scale if widget | ||
341 | pmScaled = pm; // size equals pixmap size | ||
342 | } else { | ||
343 | double hs = (double)h / (double)image.height(); | ||
344 | double ws = (double)width() / (double)image.width(); | ||
345 | double scaleFactor = (hs > ws) ? ws : hs; | ||
346 | int smoothW = (int)(scaleFactor * image.width()); | ||
347 | int smoothH = (int)(scaleFactor * image.height()); | ||
348 | |||
349 | pmScaled.convertFromImage( image.smoothScale( smoothW, smoothH ) /*, conversion_flags */ ); | ||
350 | } | ||
351 | QApplication::restoreOverrideCursor();// restore original cursor | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | The resize event handler, if a valid pixmap was loaded it will call | ||
356 | scale() to fit the pixmap to the new widget size. | ||
357 | */ | ||
358 | |||
359 | void ImageViewer::resizeEvent( QResizeEvent * ) | ||
360 | { | ||
361 | imagePanel->statusLabel()->setGeometry(0, height() - imagePanel->statusLabel()->height(), | ||
362 | width(), imagePanel->statusLabel()->height()); | ||
363 | |||
364 | if ( pm.size() == QSize( 0, 0 ) ) // we couldn't load the image | ||
365 | return; | ||
366 | |||
367 | int h = calcHeight(); | ||
368 | |||
369 | if ( width() != pmScaled.width() || h != pmScaled.height()) | ||
370 | { // if new size, | ||
371 | scale(); // scale pmScaled to window | ||
372 | updateStatus(); | ||
373 | } | ||
374 | if ( image.hasAlphaBuffer() ) | ||
375 | erase(); | ||
376 | } | ||
377 | |||
378 | void ImageViewer::convertEvent( QMouseEvent* e, int& x, int& y) | ||
379 | { | ||
380 | if ( pm.size() != QSize( 0, 0 ) ) { | ||
381 | int h = height() - menubar->heightForWidth( width() ) - imagePanel->statusLabel()->height(); | ||
382 | int nx = e->x() * image.width() / width(); | ||
383 | int ny = (e->y()-menubar->heightForWidth( width() )) * image.height() / h; | ||
384 | if (nx != x || ny != y ) { | ||
385 | x = nx; | ||
386 | y = ny; | ||
387 | updateStatus(); | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | void ImageViewer::mousePressEvent( QMouseEvent *e ) | ||
393 | { | ||
394 | convertEvent(e, clickx, clicky); | ||
395 | } | ||
396 | |||
397 | void ImageViewer::mouseMoveEvent( QMouseEvent *e ) | ||
398 | { | ||
399 | convertEvent( e, pickx, picky ); | ||
400 | } | ||
401 | |||
402 | void ImageViewer::hFlip() | ||
403 | { | ||
404 | if ( loadSelected() ) | ||
405 | setImage(image.mirror(TRUE,FALSE)); | ||
406 | } | ||
407 | |||
408 | void ImageViewer::vFlip() | ||
409 | { | ||
410 | if ( loadSelected() ) | ||
411 | setImage(image.mirror(FALSE,TRUE)); | ||
412 | } | ||
413 | |||
414 | void ImageViewer::rot180() | ||
415 | { | ||
416 | if ( loadSelected() ) | ||
417 | setImage(image.mirror(TRUE,TRUE)); | ||
418 | } | ||
419 | |||
420 | void ImageViewer::rot90() | ||
421 | { | ||
422 | if ( loadSelected() ) { | ||
423 | QImage oldimage, newimage; | ||
424 | uchar *oldbits, *newbits; | ||
425 | int i, j, p; | ||
426 | int w, h; | ||
427 | |||
428 | oldimage = image.convertDepth(32); | ||
429 | w = oldimage.height(); | ||
430 | h = oldimage.width(); | ||
431 | newimage = QImage( w, h, 32); | ||
432 | |||
433 | oldbits = oldimage.bits(); | ||
434 | newbits = newimage.bits(); | ||
435 | |||
436 | for (i=0; i < w ; i++) | ||
437 | for (j=0; j < h; j++) | ||
438 | for (p = 0 ; p < 4 ; p++) | ||
439 | newbits[(j * w + i) * 4 + p] = oldbits[ ((i + 1) * h - j ) * 4 + p]; | ||
440 | |||
441 | setImage(newimage); | ||
442 | } | ||
443 | } | ||
444 | |||
445 | |||
446 | |||
447 | void ImageViewer::normalView() | ||
448 | { | ||
449 | if ( !imagePanel->parentWidget() ) { | ||
450 | isFullScreen = FALSE; | ||
451 | stack->addWidget( imagePanel, 1 ); | ||
452 | //imagePanel->reparent(stack,0,QPoint(0,0),FALSE); | ||
453 | //imagePanel->resize(width(), calcHeight()); | ||
454 | scale(); | ||
455 | updateStatus(); | ||
456 | imagePanel->setPixmap( pmScaled ); | ||
457 | imagePanel->showStatus(); | ||
458 | //imagePanel->show(); | ||
459 | stack->raiseWidget( imagePanel ); | ||
460 | } | ||
461 | } | ||
462 | |||
463 | void ImageViewer::fullScreen() | ||
464 | { | ||
465 | // Full-screen and rotation options | ||
466 | // contributed by Robert Wittams <robert@wittams.com> | ||
467 | |||
468 | if ( imagePanel->parentWidget() && loadSelected() ) { | ||
469 | isFullScreen = TRUE; | ||
470 | imagePanel->reparent(0,QPoint(0,0)); | ||
471 | imagePanel->resize(qApp->desktop()->width(), qApp->desktop()->height()); | ||
472 | |||
473 | scale(); | ||
474 | updateStatus(); | ||
475 | imagePanel->hideStatus(); | ||
476 | imagePanel->setPixmap( pmScaled ); | ||
477 | imagePanel->showFullScreen(); | ||
478 | } | ||
479 | } | ||
480 | |||
481 | void ImageViewer::setImage(const QImage& newimage) | ||
482 | { | ||
483 | image = newimage; | ||
484 | pickx = -1; | ||
485 | clickx = -1; | ||
486 | reconvertImage(); | ||
487 | imagePanel->setPixmap( pmScaled ); | ||
488 | updateStatus(); | ||
489 | } | ||
490 | |||
491 | void ImageViewer::updateStatus() | ||
492 | { | ||
493 | if ( pm.size() == QSize( 0, 0 ) ) { | ||
494 | if ( filename ) | ||
495 | imagePanel->statusLabel()->setText( tr("Could not load image") ); | ||
496 | else | ||
497 | imagePanel->statusLabel()->setText( tr("No image - select Open from File menu.") ); | ||
498 | } else { | ||
499 | QString message("%1x%2"); | ||
500 | message = message.arg(image.width()).arg(image.height()); | ||
501 | if ( pm.size() != pmScaled.size() ) | ||
502 | message += QString(" [%1x%2]").arg(pmScaled.width()).arg(pmScaled.height()); | ||
503 | if (image.valid(pickx,picky)) { | ||
504 | QString moremsg; | ||
505 | moremsg.sprintf("(%d,%d)=#%0*x ", | ||
506 | pickx, picky, | ||
507 | image.hasAlphaBuffer() ? 8 : 6, | ||
508 | image.pixel(pickx,picky)); | ||
509 | message += moremsg; | ||
510 | } | ||
511 | if ( image.numColors() > 0 ) { | ||
512 | if (image.valid(pickx,picky)) { | ||
513 | message += tr(", %1/%2 colors") | ||
514 | .arg(image.pixelIndex(pickx,picky)) | ||
515 | .arg(image.numColors()); | ||
516 | } else { | ||
517 | message += tr(", %1 colors").arg(image.numColors()); | ||
518 | } | ||
519 | } else if ( image.depth() >= 16 ) { | ||
520 | message += tr(" True color"); | ||
521 | } | ||
522 | if ( image.hasAlphaBuffer() ) { | ||
523 | if ( image.depth() == 8 ) { | ||
524 | int i; | ||
525 | bool alpha[256]; | ||
526 | int nalpha=0; | ||
527 | |||
528 | for (i=0; i<256; i++) | ||
529 | alpha[i] = FALSE; | ||
530 | |||
531 | for (i=0; i<image.numColors(); i++) { | ||
532 | int alevel = image.color(i) >> 24; | ||
533 | if (!alpha[alevel]) { | ||
534 | alpha[alevel] = TRUE; | ||
535 | nalpha++; | ||
536 | } | ||
537 | } | ||
538 | message += tr(", %1 alpha levels").arg(nalpha); | ||
539 | } else { | ||
540 | // Too many pixels to bother counting. | ||
541 | message += tr(", 8-bit alpha channel"); | ||
542 | } | ||
543 | } | ||
544 | imagePanel->statusLabel()->setText(message); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | void ImageViewer::closeEvent( QCloseEvent *e ) | ||
549 | { | ||
550 | if ( stack->visibleWidget() == imagePanel && !bFromDocView ) { | ||
551 | e->ignore(); | ||
552 | open(); | ||
553 | } else { | ||
554 | bFromDocView = FALSE; | ||
555 | e->accept(); | ||
556 | } | ||
557 | } | ||