summaryrefslogtreecommitdiffstats
path: root/src/sq_glwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sq_glwidget.cpp')
-rw-r--r--src/sq_glwidget.cpp2006
1 files changed, 2006 insertions, 0 deletions
diff --git a/src/sq_glwidget.cpp b/src/sq_glwidget.cpp
new file mode 100644
index 0000000..0380090
--- /dev/null
+++ b/src/sq_glwidget.cpp
@@ -0,0 +1,2006 @@
+/***************************************************************************
+ sq_glwidget.cpp - description
+ -------------------
+ begin : Mon Mar 15 2004
+ copyright : (C) 2004 by Baryshev Dmitry
+ email : ksquirrel.iv@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <tqeventloop.h>
+#include <tqstringlist.h>
+#include <tqfileinfo.h>
+#include <tqdatetime.h>
+#include <tqlabel.h>
+#include <tqtimer.h>
+#include <tqdatetime.h>
+#include <tqslider.h>
+#include <tqlabel.h>
+
+#include <tdeapplication.h>
+#include <tdeaction.h>
+#include <kcursor.h>
+#include <kstandarddirs.h>
+#include <kstatusbar.h>
+#include <tdefiledialog.h>
+#include <tdeglobal.h>
+#include <tdelocale.h>
+#include <kstringhandler.h>
+#include <tdemessagebox.h>
+#include <kdebug.h>
+#include <tdeio/job.h>
+#include <tdetempfile.h>
+#include <twin.h>
+#include <ktabbar.h>
+
+#include <cmath>
+#include <cstdlib>
+
+#ifndef KSQUIRREL_PART
+#include "ksquirrel.h"
+#include "sq_widgetstack.h"
+#include "sq_glview.h"
+#include "sq_fileiconview.h"
+#include "sq_diroperator.h"
+#include "sq_hloptions.h"
+#include "sq_previewwidget.h"
+#include "sq_tabopendialog.h"
+#endif
+
+#include "sq_popupmenu.h"
+#include "sq_libraryhandler.h"
+#include "sq_config.h"
+#include "sq_glwidget_helpers.h"
+#include "sq_glwidget.h"
+#include "sq_glu.h"
+#include "sq_codecsettings.h"
+#include "sq_glselectionpainter.h"
+#include "sq_utils.h"
+#include "sq_errorstring.h"
+#include "fmt_filters.h"
+
+#include <ksquirrel-libs/fileio.h>
+#include <ksquirrel-libs/fmt_codec_base.h>
+#include <ksquirrel-libs/fmt_utils.h>
+#include <ksquirrel-libs/error.h>
+
+#ifdef SQ_HAVE_KEXIF
+#include <libkexif/kexifdata.h>
+#endif
+
+// is it enough ?
+#define SQ_FLOAT_SIGMA 1e-5
+
+#define ISFLOAT(a,b) (fabs(a - b) < SQ_FLOAT_SIGMA)
+#define ISFLOAT1(a) ISFLOAT(a, 1.0)
+#define ISFLOAT0(a) ISFLOAT(a, 0.0)
+
+#define SQ_TAB_TEXT_LENGTH 30
+
+/* ***************************************************************************************** */
+
+SQ_GLWidget * SQ_GLWidget::m_instance = 0;
+
+static const int timer_delay_file = 5;
+static const double rad_const = 3.14159265358979323846 / 180.0;
+
+static const float SQ_WINDOW_BACKGROUND_POS = -1000.0;
+static const float SQ_IMAGE_CHECKER_POS = -999.0;
+static const float SQ_FIRST_FRAME_POS = -998.0;
+static const float SQ_MARKS_POS = -997.0;
+static const float SQ_FIRST_TILE_LAYER = -995.0;
+static const float SQ_ONSCREEN_LAYER = 10000.0;
+
+/* ***************************************************************************************** */
+
+SQ_GLWidget::SQ_GLWidget(TQWidget *parent, const char *name) : TQGLWidget(parent, name)
+{
+ kdDebug() << "+SQ_GLWidget" << endl;
+
+ // init all variables...
+ m_instance = this;
+
+#ifdef KSQUIRREL_PART
+ connect(&t_glv, TQ_SIGNAL(message(const TQString &)), this, TQ_SIGNAL(message(const TQString &)));
+#endif
+
+ zoomMenu = 0;
+ selectionMenu = 0;
+ images = 0;
+ parts_broken = 0;
+
+ ac = new TDEActionCollection(this, this, "GLWidget actionCollection");
+ changed = blocked = decoded = reset_mode = false;
+ movetype = -1;
+ buffer = new RGBA [512 * 512];
+ zoomFactor = 1.0;
+ old_id = -1;
+ menu = new TQPopupMenu(this);
+ hackResizeGL = false;
+ lastCopy = KURL::fromPathOrURL("/");
+ oldZoom = -1;
+
+ percentsLabel = new TQLabel(this);
+ percentsLabel->move(4, 4);
+ percentsLabel->hide();
+
+ tabold = tab = &taborig;
+
+ tmp = new KTempFile;
+ tmp->setAutoDelete(true);
+ tmp->close();
+
+ SQ_Config::instance()->setGroup("GL view");
+ zoom_type = SQ_Config::instance()->readNumEntry("zoom type", 2);
+ linear = SQ_Config::instance()->readBoolEntry("zoom_nice", true);
+
+ // load background for transparent image
+ BGquads = TQImage(locate("data", "images/checker.png"));
+
+ if(BGquads.isNull())
+ {
+ BGquads = TQImage(32, 32, 32);
+ BGquads.setAlphaBuffer(true);
+ BGquads.fill(0);
+ }
+
+ bindChecker = true;
+
+ zoomfactor = SQ_Config::instance()->readNumEntry("zoom", 25);
+ movefactor = SQ_Config::instance()->readNumEntry("move", 5);
+ rotatefactor = SQ_Config::instance()->readNumEntry("angle", 90);
+
+ // load cursors
+ setCursor(KCursor::arrowCursor());
+
+ setFocusPolicy(TQWidget::WheelFocus);
+ setAcceptDrops(true);
+
+ // popup menu with image pages
+ images = new TDEPopupMenu;
+ images->setCheckable(true);
+
+ // create actions
+ createActions();
+
+ // create toolbars
+ createToolbar();
+
+ // create tickmarks
+ createMarks();
+
+ initAccelsAndMenu();
+
+ enableActions(false);
+
+ KCursor::setAutoHideCursor(this, true);
+ KCursor::setHideCursorDelay(2500);
+
+#ifndef KSQUIRREL_PART
+ timer_prev = new TQTimer(this);
+ timer_next = new TQTimer(this);
+
+ TQ_CHECK_PTR(timer_prev);
+ TQ_CHECK_PTR(timer_next);
+
+ connect(timer_prev, TQ_SIGNAL(timeout()), SQ_WidgetStack::instance(), TQ_SLOT(emitPreviousSelected()));
+ connect(timer_next, TQ_SIGNAL(timeout()), SQ_WidgetStack::instance(), TQ_SLOT(emitNextSelected()));
+#endif
+
+ timer_anim = new TQTimer(this);
+
+ TQ_CHECK_PTR(timer_anim);
+
+ connect(timer_anim, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotAnimateNext()));
+
+ connect(images, TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotSetCurrentImage(int)));
+ connect(images, TQ_SIGNAL(aboutToHide()), this, TQ_SLOT(slotImagesHidden()));
+ connect(images, TQ_SIGNAL(aboutToShow()), this, TQ_SLOT(slotImagesShown()));
+
+ gls = new SQ_GLSelectionPainter(this);
+}
+
+SQ_GLWidget::~SQ_GLWidget()
+{
+ kdDebug() << "-SQ_GLWidget" << endl;
+
+ delete gls;
+
+ delete parts_broken;
+
+ removeCurrentTabs();
+
+ delete selectionMenu;
+ delete zoomMenu;
+ delete images;
+ delete [] buffer;
+ delete tmp;
+}
+
+// Initialize OpenGL context. Used internally by TQGLWidget.
+void SQ_GLWidget::initializeGL()
+{
+ setClearColor();
+ glClearDepth(1.0);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_ALPHA_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glShadeModel(GL_FLAT);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // finally, clear
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ // create "broken" image
+ initBrokenImage();
+
+ initMarks();
+}
+
+// Resize OpenGL context. Used internally by TQGLWidget.
+void SQ_GLWidget::resizeGL(int width, int height)
+{
+ gls->setSourceSize(width, height);
+
+ // set new viewport
+ glViewport(0, 0, width, height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ // O(0,0) will be in the center of the window
+ glOrtho(-width/2, width/2, -height/2, height/2, 0.1f, 10000.0);
+
+ // camera setup
+ SQ_GLU::gluLookAt(0,0,1, 0,0,0, 0,1,0);
+ glMatrixMode(GL_MODELVIEW);
+
+ if(decoded && !hackResizeGL)
+ slotZoomIfLess();
+
+ hackResizeGL = false;
+}
+
+/*
+ * Fill a w x h region with texture. Generate texture if needed.
+ */
+void SQ_GLWidget::draw_background(void *bits, unsigned int *tex, int dim, GLfloat w, GLfloat h, bool &bind, bool deleteOld)
+{
+ float half_w, half_h;
+
+ half_w = w / 2.0;
+ half_h = h / 2.0;
+
+ // bind texture ?
+ if(bind)
+ {
+ // delete old texture before binding ?
+ if(deleteOld)
+ glDeleteTextures(1, tex);
+
+ // generate and bind texture
+ glGenTextures(1, tex);
+ glBindTexture(GL_TEXTURE_2D, *tex);
+
+ // setup parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ // assign image to texture
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim, dim, 0, GL_RGBA, GL_UNSIGNED_BYTE, bits);
+
+ bind = false;
+ }
+ else
+ glBindTexture(GL_TEXTURE_2D, *tex);
+
+ // draw
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0, 0.0); glVertex2f(-half_w, half_h);
+ glTexCoord2f(w/(GLfloat)dim, 0.0); glVertex2f(half_w, half_h);
+ glTexCoord2f(w/(GLfloat)dim, h/(GLfloat)dim); glVertex2f(half_w, -half_h);
+ glTexCoord2f(0.0, h/(GLfloat)dim); glVertex2f(-half_w, -half_h);
+ glEnd();
+}
+
+// Redraw OpenGL context. Used internally by TQGLWidget.
+void SQ_GLWidget::paintGL()
+{
+ int z;
+
+ // clear
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if(gls->valid())
+ {
+ matrix_push();
+ matrix_pure_reset();
+ TQPoint p = gls->center();
+
+ // move to selection center
+ MATRIX_X = p.x();
+ MATRIX_Y = p.y();
+ write_gl_matrix();
+
+ gls->draw();
+
+ matrix_pop();
+ write_gl_matrix();
+ }
+
+ glEnable(GL_TEXTURE_2D);
+
+ // draw window background ?
+ SQ_Config::instance()->setGroup("GL view");
+ if(SQ_Config::instance()->readNumEntry("GL view background type", 1) == 2)
+ {
+ static bool del = false;
+ matrix_push();
+ matrix_pure_reset();
+ matrix_move_z(SQ_WINDOW_BACKGROUND_POS);
+ draw_background(BGpixmap.bits(), &texPixmap, BGpixmap.width(), width(), height(), changed, del);
+ del = true;
+ matrix_pop();
+ write_gl_matrix();
+ }
+
+ // draw image
+ if(!reset_mode && decoded)
+ {
+ SQ_Config::instance()->setGroup("GL view");
+
+ fmt_image *im = &tab->finfo.image[tab->current];
+
+ // if the image is transparent, and we should draw background for image
+ if(im->hasalpha && SQ_Config::instance()->readBoolEntry("alpha_bkgr", true))
+ {
+ GLfloat w = (float)im->w / 2.0, h = (float)im->h / 2.0;
+
+ static const GLdouble eq[4][4] =
+ {
+ {0.0, 1.0, 0.0, 0.0},
+ {1.0, 0.0, 0.0, 0.0},
+ {0.0, -1.0, 0.0, 0.0},
+ {-1.0, 0.0, 0.0, 0.0}
+ };
+
+ // we will draw background for transparent image (quads) within
+ // entire window, and cut off useless regions with clip planes
+ glPushMatrix();
+ glTranslatef(-w, -h, 0.0);
+ glClipPlane(GL_CLIP_PLANE0, eq[0]);
+ glClipPlane(GL_CLIP_PLANE1, eq[1]);
+ glEnable(GL_CLIP_PLANE0);
+ glEnable(GL_CLIP_PLANE1);
+ glPopMatrix();
+ glPushMatrix();
+ glTranslatef(w, h, 0.0);
+ glClipPlane(GL_CLIP_PLANE2, eq[2]);
+ glClipPlane(GL_CLIP_PLANE3, eq[3]);
+ glEnable(GL_CLIP_PLANE2);
+ glEnable(GL_CLIP_PLANE3);
+ glPopMatrix();
+
+ // draw background
+ matrix_push();
+ matrix_pure_reset();
+ matrix_move_z(SQ_IMAGE_CHECKER_POS);
+ draw_background(BGquads.bits(), &texQuads, 32, width(), height(), bindChecker, !bindChecker);
+ matrix_pop();
+ write_gl_matrix();
+
+ // don't need planes any more...
+ glDisable(GL_CLIP_PLANE3);
+ glDisable(GL_CLIP_PLANE2);
+ glDisable(GL_CLIP_PLANE1);
+ glDisable(GL_CLIP_PLANE0);
+ }
+
+ matrix_move_z(SQ_FIRST_FRAME_POS);
+
+ Parts *pt = tab->broken ? parts_broken : &tab->parts[tab->current];
+
+ // draw current image
+ int toy = pt->tilesy.size();
+ int tox = pt->tilesx.size();
+ for(z = 0;z < toy;z++)
+ if(glIsList(pt->m_parts[z * tox].list))
+ glCallList(pt->m_parts[z * tox].list);
+
+ // draw tickmarks ("broken" image won't have tickmarks)
+ if(!tab->broken && marks && SQ_Config::instance()->readBoolEntry("marks", true))
+ {
+ GLfloat zum = getZoom();
+ GLfloat x = fabsf(pt->m_parts[0].x1) * zum, y = pt->m_parts[0].y1 * zum;
+ GLfloat X = MATRIX_X, Y = MATRIX_Y;
+
+ if(x < 0.0)
+ x = -x;
+
+ const GLfloat ly = y+16, ry = -y-16;
+ const GLfloat lx = x+16, rx = -x-16;
+
+ matrix_push();
+ matrix_pure_reset();
+ MATRIX_X = X;
+ MATRIX_Y = Y;
+ matrix_rotate2(tab->curangle);
+ matrix_move_z(SQ_MARKS_POS);
+
+ GLfloat coords[4][8] =
+ {
+ {rx, ly, -x, ly, -x, y, rx, y},
+ {x, ly, lx, ly, lx, y, x, y},
+ {x, -y, lx, -y, lx, ry, x, ry},
+ {rx, -y, -x, -y, -x, ry, rx, ry}
+ };
+
+ for(z = 0;z < 4;z++)
+ {
+ glBindTexture(GL_TEXTURE_2D, mark[z]);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0, 0.0); glVertex2f(coords[z][0], coords[z][1]);
+ glTexCoord2f(1.0, 0.0); glVertex2f(coords[z][2], coords[z][3]);
+ glTexCoord2f(1.0, 1.0); glVertex2f(coords[z][4], coords[z][5]);
+ glTexCoord2f(0.0, 1.0); glVertex2f(coords[z][6], coords[z][7]);
+ glEnd();
+ }
+
+ matrix_pop();
+ write_gl_matrix();
+ }
+ }
+
+ glDisable(GL_TEXTURE_2D);
+
+ matrixChanged();
+
+ if(!tab->broken && tab->total > 1)
+ frameChanged();
+}
+
+/*
+ * Change statusbar info according with
+ * current matrix (it shows current zoom & angle values).
+ */
+void SQ_GLWidget::matrixChanged()
+{
+ TQString str;
+
+ float m = getZoom();
+ float fzoom = m * 100.0;
+ float z = (m < 1.0) ? 1.0/m : m;
+
+ // construct zoom
+ str = TQString::fromLatin1("%1% [%2:%3]")
+ .arg(fzoom, 0, 'f', 1)
+ .arg((m < 1.0)?1.0:z, 0, 'f', 1)
+ .arg((m > 1.0)?1.0:z, 0, 'f', 1);
+
+#ifndef KSQUIRREL_PART
+ SQ_GLView::window()->sbarWidget("SBGLZoom")->setText(str);
+#else
+ t_glv.sbarWidget("SBGLZoom")->setText(str);
+#endif
+
+ // construct rotation angle
+ str = TQString::fromLatin1("%1%2 %3 deg")
+ .arg((tab->isflippedV)?"V":"")
+ .arg((tab->isflippedH)?"H":"")
+ .arg(tab->curangle, 0, 'f', 1);
+
+#ifndef KSQUIRREL_PART
+ SQ_GLView::window()->sbarWidget("SBGLAngle")->setText(str);
+#else
+ t_glv.sbarWidget("SBGLAngle")->setText(str);
+#endif
+}
+
+/*
+ * Mouse wheel event. Let's load next/previous image, or
+ * zoom in/zoom out (depends on settings).
+ */
+void SQ_GLWidget::wheelEvent(TQWheelEvent *e)
+{
+ if(e->delta() < 0 && e->state() == TQt::NoButton)
+ {
+#ifndef KSQUIRREL_PART
+ SQ_Config::instance()->setGroup("GL view");
+
+ // load next file in current directory
+ if(SQ_Config::instance()->readNumEntry("scroll", 0))
+ slotNext();
+ else
+#endif
+
+ slotZoomPlus();
+ }
+ else if(e->delta() > 0 && e->state() == TQt::NoButton)
+ {
+#ifndef KSQUIRREL_PART
+ SQ_Config::instance()->setGroup("GL view");
+
+ if(SQ_Config::instance()->readNumEntry("scroll", 0))
+ slotPrev();
+ else
+#endif
+
+ slotZoomMinus();
+ }
+ // some special bindings:
+ // if CTRL key is pressed, zoom 2x or 0.5x
+ else if(e->delta() < 0 && e->state() == TQt::ControlButton)
+ matrix_zoom(2.0);
+ else if(e->delta() > 0 && e->state() == TQt::ControlButton)
+ matrix_zoom(0.5f);
+ else if(e->delta() < 0 && e->state() == TQt::ShiftButton)
+ slotZoomPlus();
+ else if(e->delta() > 0 && e->state() == TQt::ShiftButton)
+ slotZoomMinus();
+}
+
+// User pressed mouse button down.
+void SQ_GLWidget::mousePressEvent(TQMouseEvent *e)
+{
+ setFocus();
+
+ // left button, update cursor
+ if(e->button() == TQt::LeftButton && e->state() == TQt::NoButton && tab->glselection == -1)
+ {
+#ifndef KSQUIRREL_PART
+ TQTime t = TQTime::currentTime();
+
+ SQ_Config::instance()->setGroup("GL view");
+ int dc = SQ_Config::instance()->readNumEntry("double_click", 0);
+
+ if(dc && clickTime.isValid() && clickTime.msecsTo(t) <= TDEApplication::doubleClickInterval())
+ {
+ if(dc == 1)
+ KSquirrel::app()->closeGLWidget();
+ else
+ toggleFullScreen();
+
+ return;
+ }
+ else
+ clickTime = t;
+
+#endif
+ setCursor(KCursor::sizeAllCursor());
+
+ xmoveold = e->x();
+ ymoveold = e->y();
+
+ movetype = 1;
+ }
+ // left button + SHIFT, let's start drawing zoom frame
+ else if(e->button() == TQt::LeftButton && (e->state() == TQt::ShiftButton || tab->glselection != -1))
+ {
+ // stop animation!
+ stopAnimation();
+
+ // update cursor to crosshair
+ setCursor(KCursor::crossCursor());
+
+ if(tab->glselection == SQ_GLSelectionPainter::Rectangle || tab->glselection == SQ_GLSelectionPainter::Ellipse)
+ gls->begin(static_cast<SQ_GLSelectionPainter::Type>(tab->glselection), e->x(), e->y());
+ else
+ gls->begin(SQ_GLSelectionPainter::Rectangle, e->x(), e->y());
+
+ movetype = 2;
+ }
+ // right button - show context menu
+ else if(e->button() == TQt::RightButton)
+ menu->popup(TQCursor::pos());
+ // middle button - toggle fullscreen state
+ else if(e->button() == TQt::MidButton)
+ toggleFullScreen();
+}
+
+// User moved mouse.
+void SQ_GLWidget::mouseMoveEvent(TQMouseEvent *e)
+{
+ // user didn't press any mouse button before ?
+ if(movetype == -1 && fullscreen())
+ {
+#if 0
+// bool tvis = SQ_GLView::window()->toolbar()->isShown();
+// bool svis = SQ_GLView::window()->statusbar()->isShown();
+
+ int h = SQ_GLView::window()->toolbar()->height() +
+ SQ_GLView::window()->tabbar()->height();
+
+#warning FIXME
+#ifndef KSQUIRREL_PART
+ hackResizeGL = true;
+ SQ_GLView::window()->boxBar()->setShown((/*tvis ? false:*/(e->y() < h)));
+ SQ_GLView::window()->statusbar()->setShown((/*svis ? false:*/(e->y() >
+ height()-SQ_GLView::window()->statusbar()->height())));
+#endif
+#endif
+ return;
+ }
+
+ // left mouse button, lets move image (or matrix :-) )
+ if(movetype == 1)
+ {
+ xmove = e->x();
+ ymove = e->y();
+
+ matrix_move(xmove-xmoveold, -ymove+ymoveold);
+
+ xmoveold = e->x();
+ ymoveold = e->y();
+ }
+ // left + SHIFT
+ else if(movetype == 2)
+ gls->move(e->x(), e->y());
+}
+
+// User released some mouse button.
+void SQ_GLWidget::mouseReleaseEvent(TQMouseEvent *)
+{
+ if(movetype == -1)
+ return;
+
+ // left button - restore cursor
+ if(movetype == 1 || (movetype == 2 && tab->glselection != -1)) // permanent selection
+ setCursor(KCursor::arrowCursor());
+ // left button + SHIFT - zoom to selected rectangle (if needed)
+ else if(movetype == 2 && tab->glselection == -1)
+ {
+ setCursor(KCursor::arrowCursor());
+
+ TQSize sz = gls->size();
+ TQPoint pt = gls->pos();
+ TQRect lastRect(pt.x(), pt.y(), sz.width(), sz.height());
+
+ gls->end();
+
+ TQPoint lastC = lastRect.center();
+ TQPoint O(width() / 2, height() / 2);
+
+ if(lastRect.width() > 2 && lastRect.height() > 2)
+ {
+ bool lastReset = reset_mode;
+ reset_mode = true;
+ float X = MATRIX_X, Y = MATRIX_Y;
+ matrix_move(O.x() - lastC.x(), lastC.y() - O.y());
+ reset_mode = lastReset;
+
+ // try to zoom
+ bool zoomed = tab->broken ? false : zoomRect(lastRect);
+
+ // not zoomed ? (zoom > maximum zoom)
+ if(!zoomed)
+ {
+ MATRIX_X = X;
+ MATRIX_Y = Y;
+ write_gl_matrix();
+ }
+ }
+
+ updateGL();
+
+ // start animation, if needed
+ if(!manualBlocked())
+ startAnimation();
+ }
+
+ movetype = -1;
+}
+
+/*
+ * Zoom to 'r'. Will be called after somebody used right mouse button
+ * to select zoom region.
+ */
+bool SQ_GLWidget::zoomRect(const TQRect &r)
+{
+ // calculate zoom factor
+ float factor = 1.0;
+ float w = (float)width(), h = (float)height();
+ float factorw = w / (float)r.width();
+ float factorh = h / (float)r.height();
+ float t = w / h;
+
+ if(t > (float)r.width() / (float)r.height())
+ factor = factorh;
+ else
+ factor = factorw;
+
+ // try to zoom
+ return matrix_zoom(factor);
+}
+
+// Fit width.
+void SQ_GLWidget::slotZoomW()
+{
+ zoom_type = 0;
+ pAZoomW->setChecked(true);
+
+ // no image decoded
+ if(tab->broken || tab->finfo.image.empty()) return;
+
+ // calculate zoom factor
+ float factor = (float)width() / (tab->rotate ? (float)tab->finfo.image[tab->current].h : (float)tab->finfo.image[tab->current].w);
+
+ // "Ignore, if the image is less than window" - restore zoom factor to 1.0
+ if(pAIfLess->isChecked() && (tab->finfo.image[tab->current].w < width() && tab->finfo.image[tab->current].h < height()))
+ factor = 1.0;
+
+ // zoom...
+ internalZoom(factor);
+}
+
+// Fit height.
+void SQ_GLWidget::slotZoomH()
+{
+ zoom_type = 1;
+ pAZoomH->setChecked(true);
+
+ if(tab->broken || tab->finfo.image.empty()) return;
+
+ float factor = (float)height() / (tab->rotate ? (float)tab->finfo.image[tab->current].w : (float)tab->finfo.image[tab->current].h);
+
+ if(pAIfLess->isChecked() && (tab->finfo.image[tab->current].w < width() && tab->finfo.image[tab->current].h < height()))
+ factor = 1.0;
+
+ internalZoom(factor);
+}
+
+// Fit image (e.g. width and height).
+void SQ_GLWidget::slotZoomWH()
+{
+ zoom_type = 2;
+ pAZoomWH->setChecked(true);
+
+ if(tab->broken || tab->finfo.image.empty()) return;
+
+ float factor = 1.0;
+ float w = (float)width(), h = (float)height();
+ float factorw = w / (tab->rotate ? (float)tab->finfo.image[tab->current].h : (float)tab->finfo.image[tab->current].w);
+ float factorh = h / (tab->rotate ? (float)tab->finfo.image[tab->current].w : (float)tab->finfo.image[tab->current].h);
+ float t = w / h;
+ float F = tab->rotate ? ((float)tab->finfo.image[tab->current].h / (float)tab->finfo.image[tab->current].w) : ((float)tab->finfo.image[tab->current].w / (float)tab->finfo.image[tab->current].h);
+
+ if(t > F)
+ factor = factorh;
+ else
+ factor = factorw;
+
+ if(pAIfLess->isChecked() && (tab->finfo.image[tab->current].w < width() && tab->finfo.image[tab->current].h < height()))
+ factor = 1.0;
+
+ internalZoom(factor);
+}
+
+// Previous zoom.
+void SQ_GLWidget::slotZoomLast()
+{
+ zoom_type = 4;
+ pAZoomLast->setChecked(true);
+
+ if(tab->broken || tab->finfo.image.empty()) return;
+
+ internalZoom(zoomFactor);
+}
+
+// Zoom 100%.
+void SQ_GLWidget::slotZoom100()
+{
+ zoom_type = 3;
+ pAZoom100->setChecked(true);
+
+ if(tab->broken || tab->finfo.image.empty()) return;
+
+ internalZoom(1.0);
+}
+
+// "Ignore, if the image is less than window"
+void SQ_GLWidget::slotZoomIfLess()
+{
+ if(tab->broken || tab->finfo.image.empty()) return;
+
+ switch(zoom_type)
+ {
+ case 0: slotZoomW(); break;
+ case 1: slotZoomH(); break;
+ case 2: slotZoomWH(); break;
+ case 3: break;
+
+ default:
+ slotZoomLast();
+ }
+}
+
+// Zoom+
+void SQ_GLWidget::slotZoomPlus()
+{
+ matrix_zoom(1.0+zoomfactor/100.0);
+}
+
+// Zoom-
+void SQ_GLWidget::slotZoomMinus()
+{
+ matrix_zoom(1.0/(1.0+zoomfactor/100.0));
+}
+
+// Rotate left.
+void SQ_GLWidget::slotRotateLeft()
+{
+ matrix_rotate(-rotatefactor);
+}
+
+// Rotate right.
+void SQ_GLWidget::slotRotateRight()
+{
+ matrix_rotate(rotatefactor);
+}
+
+/*
+ operations with matrices are taken from GLiv =)
+
+ thanks to Guillaume Chazarian.
+*/
+void SQ_GLWidget::flip(int id, bool U)
+{
+ GLfloat x = MATRIX_X, y = MATRIX_Y;
+ MATRIX_X = 0;
+ MATRIX_Y = 0;
+
+ tab->matrix[id] *= -1.0;
+ tab->matrix[id + 1] *= -1.0;
+ tab->matrix[id + 3] *= -1.0;
+
+ MATRIX_X = x;
+ MATRIX_Y = y;
+
+ write_gl_matrix();
+
+ if(!reset_mode && U)
+ updateGL();
+}
+
+void SQ_GLWidget::slotFlipH()
+{
+ if(!tab->broken)
+ {
+ tab->isflippedH = !tab->isflippedH;
+ flip(0);
+ }
+}
+
+void SQ_GLWidget::slotFlipV()
+{
+ if(!tab->broken)
+ {
+ tab->isflippedV = !tab->isflippedV;
+ flip(4);
+ }
+}
+
+void SQ_GLWidget::slotMatrixReset()
+{
+ if(!tab->broken)
+ {
+ oldZoom = getZoom();
+ matrix_reset(false);
+ matrix_zoom(1.0);
+ }
+}
+
+void SQ_GLWidget::write_gl_matrix()
+{
+ GLfloat transposed[16] =
+ {
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0
+ };
+
+ transposed[0] = MATRIX_C1;
+ transposed[5] = MATRIX_C2;
+ transposed[4] = MATRIX_S1;
+ transposed[1] = MATRIX_S2;
+ transposed[12] = MATRIX_X;
+ transposed[13] = MATRIX_Y;
+ transposed[14] = MATRIX_Z;
+
+ glLoadMatrixf(transposed);
+}
+
+void SQ_GLWidget::matrix_move(GLfloat x, GLfloat y)
+{
+ if(tab->broken) return;
+
+ MATRIX_X += x;
+ MATRIX_Y += y;
+
+ write_gl_matrix();
+
+ if(!reset_mode)
+ updateGL();
+}
+
+void SQ_GLWidget::matrix_move_z(GLfloat z)
+{
+ MATRIX_Z = z;
+
+ write_gl_matrix();
+}
+
+void SQ_GLWidget::matrix_push()
+{
+ memcpy(saved, tab->matrix, sizeof(tab->matrix));
+}
+
+void SQ_GLWidget::matrix_pop()
+{
+ memcpy(tab->matrix, saved, sizeof(tab->matrix));
+}
+
+void SQ_GLWidget::matrix_reset(bool U)
+{
+ tab->nullMatrix();
+
+ tab->curangle = 0.0;
+ tab->isflippedH = tab->isflippedV = false;
+
+ if(decoded)
+ exifRotate(U);
+}
+
+void SQ_GLWidget::matrix_pure_reset()
+{
+ tab->nullMatrix();
+ write_gl_matrix();
+}
+
+bool SQ_GLWidget::matrix_zoom(GLfloat ratio)
+{
+ if(tab->broken) return false;
+
+ SQ_Config::instance()->setGroup("GL view");
+
+ int zoom_lim = SQ_Config::instance()->readNumEntry("zoom limit", 1);
+ GLfloat zoom_min, zoom_max, zoom_tobe;
+
+ zoom_tobe = hypot(MATRIX_C1 * ratio, MATRIX_S1 * ratio) * 100.0;
+
+ switch(zoom_lim)
+ {
+ case 2:
+ zoom_min = (float)SQ_Config::instance()->readNumEntry("zoom_min", 1);
+ zoom_max = (float)SQ_Config::instance()->readNumEntry("zoom_max", 10000);
+ break;
+
+ default: // "case 1:" too
+ zoom_min = 1.0;
+ zoom_max = 10000.0;
+ }
+
+ if(zoom_lim)
+ {
+ float z = getZoomPercents();
+
+ // zoom limit exceeded - do nothing
+ if((z >= zoom_max && ratio > 1.0) || (z <= zoom_min && ratio < 1.0))
+ return false;
+
+ // if the new zoom will be greater (smaller) than maximum
+ // (minimum) zoom - scale it.
+ if(ratio < 1.0 && zoom_min >= zoom_tobe)
+ ratio = ratio * zoom_min / zoom_tobe;
+ else if(ratio > 1.0 && zoom_max <= zoom_tobe)
+ ratio = ratio * zoom_max / zoom_tobe;
+ }
+
+ GLfloat oldz = (oldZoom == -1 ? getZoom() : oldZoom);
+
+ MATRIX_C1 *= ratio;
+ MATRIX_S1 *= ratio;
+ MATRIX_X *= ratio;
+ MATRIX_S2 *= ratio;
+ MATRIX_C2 *= ratio;
+ MATRIX_Y *= ratio;
+
+ hackMatrix();
+
+ GLfloat z = getZoom();
+ int filter = -1;
+
+ if(ISFLOAT1(oldz) && !ISFLOAT1(z))
+ filter = linear ? GL_LINEAR : GL_NEAREST;
+ else if(ISFLOAT1(z))
+ filter = GL_NEAREST;
+
+ // update all textures
+ if(filter != -1)
+ {
+ for(int i = 0;i < tab->total;i++)
+ {
+ int toxy = tab->parts[i].m_parts.size();
+
+ for(int j = 0;j < toxy;j++)
+ {
+ glBindTexture(GL_TEXTURE_2D, tab->parts[i].m_parts[j].tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ }
+ }
+ }
+
+ oldZoom = -1;
+ write_gl_matrix();
+
+ changeSlider(z);
+
+ if(!reset_mode)
+ updateGL();
+
+ return true;
+}
+
+/*
+ * Floating point operations are not 100% exact.
+ * We should correct matrix values.
+ */
+void SQ_GLWidget::hackMatrix()
+{
+ if(ISFLOAT1(MATRIX_C1)) MATRIX_C1 = MATRIX_C1 < 0 ? -1.0 : 1.0;
+ else if(ISFLOAT0(MATRIX_C1)) MATRIX_C1 = 0.0;
+
+ if(ISFLOAT1(MATRIX_C2)) MATRIX_C2 = MATRIX_C2 < 0 ? -1.0 : 1.0;
+ else if(ISFLOAT0(MATRIX_C2)) MATRIX_C2 = 0.0;
+
+ if(ISFLOAT1(MATRIX_S1)) MATRIX_S1 = MATRIX_S1 < 0 ? -1.0 : 1.0;
+ else if(ISFLOAT0(MATRIX_S1)) MATRIX_S1 = 0.0;
+
+ if(ISFLOAT1(MATRIX_S2)) MATRIX_S2 = MATRIX_S2 < 0 ? -1.0 : 1.0;
+ else if(ISFLOAT0(MATRIX_S2)) MATRIX_S2 = 0.0;
+
+ if(ISFLOAT0(MATRIX_X)) MATRIX_X = 0.0;
+ if(ISFLOAT0(MATRIX_Y)) MATRIX_Y = 0.0;
+}
+
+GLfloat SQ_GLWidget::getZoom() const
+{
+ return hypot(MATRIX_C1, MATRIX_S1);
+}
+
+GLfloat SQ_GLWidget::getZoomPercents() const
+{
+ return getZoom() * 100.0;
+}
+
+void SQ_GLWidget::matrix_rotate(GLfloat angle, bool U)
+{
+ if(tab->broken) return;
+
+ GLfloat c1 = MATRIX_C1, c2 = MATRIX_C2, s1 = MATRIX_S1, s2 = MATRIX_S2;
+
+ double rad = angle * rad_const;
+ double cosine = cos(rad);
+ double sine = sin(rad);
+
+ MATRIX_C1 = c1 * cosine + s2 * sine;
+ MATRIX_S1 = s1 * cosine + c2 * sine;
+ MATRIX_S2 = -c1 * sine + s2 * cosine;
+ MATRIX_C2 = -s1 * sine + c2 * cosine;
+
+ hackMatrix();
+
+ tab->curangle += angle;
+
+ if(tab->curangle == 360.0 || tab->curangle == -360.0)
+ tab->curangle = 0.0;
+ else if(tab->curangle > 360.0)
+ tab->curangle -= 360.0;
+ else if(tab->curangle < -360.0)
+ tab->curangle += 360.0;
+
+ write_gl_matrix();
+
+ if(U)
+ updateGL();
+}
+
+void SQ_GLWidget::matrix_rotate2(GLfloat angle)
+{
+ GLfloat c1 = MATRIX_C1, c2 = MATRIX_C2, s1 = MATRIX_S1, s2 = MATRIX_S2;
+
+ double rad = angle * rad_const;
+ double cosine = cos(rad);
+ double sine = sin(rad);
+
+ MATRIX_C1 = c1 * cosine + s2 * sine;
+ MATRIX_S1 = s1 * cosine + c2 * sine;
+ MATRIX_S2 = -c1 * sine + s2 * cosine;
+ MATRIX_C2 = -s1 * sine + c2 * cosine;
+
+ write_gl_matrix();
+}
+
+/*
+ * Bind textures, draw them and create GL lists.
+ * Also show textures by executing GL lists, if 'swap' = true.
+ */
+bool SQ_GLWidget::showFrames(int i, Parts *p, bool swap)
+{
+ int z, k = 0;
+ const int a = p->tilesx.size() * i, b = a + p->tilesx.size();
+ int filter = linear ? GL_LINEAR : GL_NEAREST;
+
+ // for safety...
+ makeCurrent();
+
+ glEnable(GL_TEXTURE_2D);
+
+ float zm = getZoom();
+
+ for(z = a;z < b;z++)
+ {
+ glBindTexture(GL_TEXTURE_2D, p->m_parts[z].tex);
+
+ // setup texture parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ISFLOAT1(zm) ? GL_NEAREST : filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ISFLOAT1(zm) ? GL_NEAREST : filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ setupBits(p, buffer, i, k);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, p->tilesx[k], p->tilesy[i], 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+
+ k++;
+ }
+
+ // create new display list
+ glNewList(p->m_parts[a].list, swap ? GL_COMPILE_AND_EXECUTE : GL_COMPILE);
+
+ // bind & draw textures (if needed)
+ for(z = a;z < b;z++)
+ {
+ glBindTexture(GL_TEXTURE_2D, p->m_parts[z].tex);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(p->m_parts[z].tx1, p->m_parts[z].ty1); glVertex2f(p->m_parts[z].x1, p->m_parts[z].y1);
+ glTexCoord2f(p->m_parts[z].tx2, p->m_parts[z].ty1); glVertex2f(p->m_parts[z].x2, p->m_parts[z].y1);
+ glTexCoord2f(p->m_parts[z].tx2, p->m_parts[z].ty2); glVertex2f(p->m_parts[z].x2, p->m_parts[z].y2);
+ glTexCoord2f(p->m_parts[z].tx1, p->m_parts[z].ty2); glVertex2f(p->m_parts[z].x1, p->m_parts[z].y2);
+ glEnd();
+ }
+
+ glEndList();
+
+ glDisable(GL_TEXTURE_2D);
+
+ // swap buffers...
+ if(swap)
+ swapBuffers();
+
+ return GL_TRUE;
+}
+
+void SQ_GLWidget::setupBits(Parts *p, RGBA *_buffer, int y, int x)
+{
+ TQPair<int, int> pair = SQ_GLWidget::calcRealDimensions(*p, y, x);
+
+ int offs = p->realw * pair.second + pair.first;
+ RGBA *orig = p->buffer->data() + offs;
+
+ int toy = p->tilesy[y];
+ int tox = p->tilesx[x];
+
+ for(int j = 0;j < toy;j++)
+ memcpy(_buffer + tox*j, orig + p->realw*j, tox*sizeof(RGBA));
+}
+
+/*
+ * Start decoding given image. We can call it from anywhere.
+ */
+void SQ_GLWidget::startDecoding(const TQString &file)
+{
+#ifndef KSQUIRREL_PART
+ if(SQ_PreviewWidget::instance()->cancel())
+ SQ_WidgetStack::instance()->diroperator()->stopPreview();
+#endif
+
+ if(reset_mode)
+ return;
+
+ started.start();
+
+ tabold = tab;
+ tmptab.empty();
+ tab = &tmptab;
+ reset_mode = true;
+
+ timer_anim->stop();
+ images->clear();
+
+ tab->m_File = file; // original name
+ tab->File = TQFile::encodeName(tab->m_File); // translated name
+ tab->m_original = m_original;
+
+ TQFileInfo fm(file);
+ tab->fmt_ext = fm.extension(false);
+ tab->fmt_size = fm.size();
+
+#ifndef KSQUIRREL_PART
+ // show window with image
+ KSquirrel::app()->raiseGLWidget();
+#endif
+
+ if(m_expected.isEmpty())
+ TDEApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput | TQEventLoop::ExcludeSocketNotifiers);
+
+ decode();
+}
+
+/*
+ * Prepare decoding. It will find proper library for decoding,
+ * clear old memory buffers, etc.
+ */
+bool SQ_GLWidget::prepare()
+{
+ TQString status;
+
+ tab = &tmptab;
+
+ // get library from cache
+ SQ_LIBRARY *m_lib = SQ_LibraryHandler::instance()->libraryForFile(tab->m_File);
+
+ if(!m_lib)
+ {
+ KMessageBox::error(this, i18n("Codec for %1 format not found").arg(tab->fmt_ext));
+ reset_mode = false;
+ tab = tabold;
+ return false;
+ }
+ else
+ tmptab.lib = m_lib;
+
+ enableActions(true);
+
+ int already = -1, result = -1, i = 0;
+ SQ_Config::instance()->setGroup("GL view");
+
+#ifndef KSQUIRREL_PART
+ bool use_tabs = SQ_GLView::window()->tabs();
+#endif
+
+ bool useCurrent = false;
+
+#ifndef KSQUIRREL_PART
+ if(!use_tabs)
+ {
+ removeCurrentTabs();
+
+ SQ_GLView::window()->removeTabs();
+ }
+ else
+ {
+ if(m_expected != m_original)
+ {
+ std::vector<Tab>::iterator it = tabs.begin();
+ std::vector<Tab>::iterator itEnd = tabs.end();
+ for(;it != itEnd;++it, ++i)
+ {
+ if((*it).m_original == m_original)
+ {
+ already = i;
+ useCurrent = true;
+ break;
+ }
+ }
+ }
+
+ // 0 - new page
+ // 1 - replace current tab
+ // 2 - close all and open in a new tab
+ // -1 - ignore 'result' at all
+ if(already == -1
+ && decoded
+ && m_expected != m_original
+ && !useCurrent
+ && SQ_Config::instance()->readBoolEntry("tabs_ask", false))
+ {
+ SQ_TabOpenDialog tbo(this);
+ tbo.exec();
+ result = tbo.result();
+ }
+ }
+#else
+ removeCurrentTabs();
+#endif
+
+#ifndef KSQUIRREL_PART
+ int curtab = SQ_GLView::window()->tabbar()->indexOf(SQ_GLView::window()->tabbar()->currentTab());
+#else
+ int curtab = -1;
+#endif
+
+ int curindex = (already == -1) ? curtab : already;
+
+ if(curindex != -1 && (m_expected == m_original || result == 1 || useCurrent))
+ {
+ if(curtab != curindex)
+ tabs[curtab].removeParts();
+
+ tabs[curindex].clearParts();
+ tabs[curindex] = tmptab;
+ tab = &tabs[curindex];
+
+#ifndef KSQUIRREL_PART
+ SQ_GLView::window()->tabbar()->blockSignals(true);
+ SQ_GLView::window()->tabbar()->setCurrentTab(SQ_GLView::window()->tabbar()->tabAt(curindex));
+ SQ_GLView::window()->tabbar()->tabAt(curindex)->setText(KStringHandler::csqueeze(tab->m_original.fileName(), SQ_TAB_TEXT_LENGTH));
+ SQ_GLView::window()->tabbar()->blockSignals(false);
+#endif
+ }
+ else
+ {
+ if(curindex != -1)
+ {
+ if(result == 2)
+ closeAllTabs();
+ else
+ tabs[curindex].removeParts();
+ }
+
+ tabs.push_back(tmptab);
+ int lastId = tabs.size() - 1;
+ tab = &tabs[lastId];
+
+#ifndef KSQUIRREL_PART
+ SQ_GLView::window()->tabbar()->blockSignals(true);
+ SQ_GLView::window()->addPage(KStringHandler::csqueeze(tab->m_original.fileName(), SQ_TAB_TEXT_LENGTH));
+ SQ_GLView::window()->tabbar()->setCurrentTab(SQ_GLView::window()->tabbar()->tabAt(lastId));
+
+ // TQTabBar::show will emit selected(int),
+ // we don't want it
+ emit tabCountChanged();
+ SQ_GLView::window()->tabbar()->blockSignals(false);
+#endif
+ }
+
+ gls->setVisible(false);
+
+ SQ_CodecSettings::applySettings(tab->lib, SQ_CodecSettings::ImageViewer);
+
+ // determine codec
+ tab->codeK = tab->lib->codec;
+
+ // start decoding!
+ i = tab->codeK->read_init(tab->File.ascii());
+
+ // oops, error...
+ if(i != SQE_OK)
+ {
+ decodeFailedOn0(i);
+ m_expected = KURL();
+ return false;
+ }
+
+ return true;
+}
+
+void SQ_GLWidget::decode()
+{
+ // prepare decoding...
+ if(!prepare())
+ return;
+
+#ifndef KSQUIRREL_PART
+ KSquirrel::app()->setCaption(originalURL());
+#endif
+
+ zoomFactor = getZoom();
+ matrix_pure_reset();
+ matrixChanged();
+
+#ifdef SQ_HAVE_KEXIF
+ KExifData d;
+ d.readFromFile(tab->m_File);
+ tab->orient = d.getImageQt::Orientation();
+ tab->wm = SQ_Utils::exifGetMatrix(TQString(), tab->orient);
+
+ tab->rotate = (tab->orient == KExifData::ROT_90_HFLIP || tab->orient == KExifData::ROT_90
+ || tab->orient == KExifData::ROT_90_VFLIP || tab->orient == KExifData::ROT_270);
+#else
+ tab->orient = -1;
+ tab->rotate = false;
+#endif
+
+ errors = 0;
+
+/* *********************************************************** */
+
+ int i, j, id;
+ int line, res, first_id = 0;
+ fmt_image *im;
+ memoryPart *pt;
+ bool progr;
+
+ SQ_Config::instance()->setGroup("GL view");
+
+ // in fullscreen mode progressive loading is disabled anyway
+ if(fullscreen())
+ progr = false;
+ else
+ progr = SQ_Config::instance()->readBoolEntry("progressiv", true);
+
+ int allpages = SQ_Config::instance()->readNumEntry("load_pages", 0);
+ int pages_num = SQ_Config::instance()->readNumEntry("load_pages_number", 1);
+
+ if(pages_num < 1) pages_num = 1;
+
+#ifndef KSQUIRREL_PART
+ SQ_GLView::window()->sbarWidget("SBFile")->setText(tab->m_original.fileName());
+#else
+ t_glv.sbarWidget("SBFile")->setText(tab->m_original.fileName());
+#endif
+
+ tab->current = 0;
+
+ bool notexpected = m_expected.isEmpty();
+ m_expected = KURL();
+
+ // start time counting
+ while(true)
+ {
+ if((allpages == 1 && tab->current) || (allpages == 2 && tab->current == pages_num))
+ break;
+
+ // absolute evil, but should do...
+ if(notexpected)
+ TDEApplication::eventLoop()->processEvents(TQEventLoop::ExcludeUserInput | TQEventLoop::ExcludeSocketNotifiers);
+
+ i = tab->codeK->read_next();
+
+ // something went wrong. SQE_NOTOK is a special type of error. It means
+ // that we decoded all pages.
+ if(i != SQE_OK)
+ {
+ if(i == SQE_NOTOK || tab->current)
+ break;
+ else
+ {
+ decodeFailedOn0(i);
+ return;
+ }
+ }
+
+ im = tab->codeK->image(tab->current);
+
+ Parts pp;
+
+ // find tile size
+ SQ_GLWidget::findCloserTiles(im->w, im->h, pp.tilesx, pp.tilesy);
+ TQPair<int, int> pair = SQ_GLWidget::calcRealDimensions(pp);
+ pp.realw = pair.first;
+ pp.realh = pair.second;
+
+ // setup current Part
+ pp.w = im->w;
+ pp.h = im->h;
+
+ // create textures and display lists
+ if(!pp.makeParts())
+ {
+ if(tab->current)
+ break;
+ else
+ {
+ KMessageBox::error(this,
+ i18n("Memory allocation failed for %1 of memory")
+ .arg(TDEIO::convertSize(pp.realw * pp.realh * sizeof(RGBA))));
+
+ decodeFailedOn0(SQE_R_NOMEMORY);
+ return;
+ }
+ }
+
+ pt = new memoryPart(pp.realw * pp.realh);
+ pt->create();
+
+ if(!pt->valid())
+ {
+ pp.removeParts();
+
+ if(tab->current)
+ break;
+ else
+ {
+ KMessageBox::error(this, i18n("Memory allocation failed"));
+ decodeFailedOn0(SQE_R_NOMEMORY);
+ return;
+ }
+ }
+
+ line = 0;
+
+ pp.computeCoords();
+ pp.buffer = pt;
+
+ tab->finfo = tab->codeK->information();
+
+ if(!tab->current)
+ {
+ tab->isflippedH = tab->isflippedV = false;
+ slotZoomIfLess();
+ matrixChanged();
+ updateCurrentFileInfo();
+ }
+
+ matrix_move_z(SQ_FIRST_TILE_LAYER+tab->current);
+
+ for(int pass = 0;pass < im->passes;pass++)
+ {
+ if(tab->codeK->read_next_pass() != SQE_OK)
+ break;
+
+ bool flip = tab->finfo.image[tab->current].needflip;
+ line = 0;
+ int tlsy = pp.tilesy.size();
+ int tlsyval, offs = 0, O, iA;
+
+ for(i = 0;i < tlsy;i++)
+ {
+ iA = flip ? (tlsy-i-1) : i;
+ tlsyval = pp.tilesy[iA];
+
+ for(j = 0;j < tlsyval;j++)
+ {
+ O = flip ? (pp.realw*(im->h - offs - j-1)) : (offs + j)*pp.realw;
+ res = tab->codeK->read_scanline(pp.buffer->data() + O);
+ errors += (int)(res != SQE_OK);
+
+ if(++line == im->h)
+ break;
+ }
+
+ offs += tlsyval;
+
+ // last pass
+ if(pass == im->passes-1)
+ {
+// if(!tab->current)
+ {
+ bool b = showFrames(iA, &pp, progr);
+
+ if(!b)
+ kdWarning() << "Showframes failed for image " << tab->current << ", tiley " << i << endl;
+ }
+ }
+
+ }
+ }
+
+ id = images->insertItem(TQString::fromLatin1("#%1 [%2x%3@%4]").arg(tab->current+1).arg(im->w).arg(im->h).arg(im->bpp));
+
+ images->setItemParameter(id, tab->current);
+
+ if(!tab->current)
+ {
+#ifndef KSQUIRREL_PART
+ SQ_GLView::window()->sbarWidget("SBDecodedI")->setPixmap(tab->lib->mime);
+#endif
+ old_id = first_id = id;
+ }
+
+ tab->parts.push_back(pp);
+
+ calcFrameLabelWidth();
+ frameChanged();
+ tab->current++;
+ }
+
+ tab->finfo = tab->codeK->information();
+ tab->codeK->read_close();
+ tab->total = tab->finfo.image.size();
+ tab->current = 0;
+ frameChanged();
+
+ enableSettingsButton(!tab->lib->config.isEmpty());
+
+ decoded = true;
+ reset_mode = false;
+ updateGL();
+
+ tab->quickImageInfo = tab->lib->quickinfo;
+ tab->elapsed = started.elapsed();
+
+#ifndef KSQUIRREL_PART
+ SQ_GLView::window()->sbarWidget("SBLoaded")->setText(TDEGlobal::locale()->formatLong(tab->elapsed) + i18n(" ms."));
+#else
+ t_glv.sbarWidget("SBLoaded")->setText(TDEGlobal::locale()->formatLong(tab->elapsed) + i18n(" ms."));
+#endif
+
+ images->setItemChecked(first_id, true);
+
+ if(tab->finfo.animated)
+ TQTimer::singleShot(tab->finfo.image[tab->current].delay, this, TQ_SLOT(slotAnimateNext()));
+}
+
+/*
+ * Palette changed. Let's update tickmarks and background color.
+ */
+void SQ_GLWidget::paletteChange(const TQPalette &oldPalette)
+{
+ TQGLWidget::paletteChange(oldPalette);
+
+ SQ_Config::instance()->setGroup("GL view");
+
+ if(SQ_Config::instance()->readNumEntry("GL view background type", 1) == 0)
+ {
+ TQColor color = colorGroup().color(TQColorGroup::Base);
+ qglClearColor(color);
+ updateGL();
+ }
+}
+
+void SQ_GLWidget::slotFirst()
+{
+#ifndef KSQUIRREL_PART
+ if(!reset_mode)
+ SQ_WidgetStack::instance()->firstFile();
+#endif
+}
+
+void SQ_GLWidget::slotLast()
+{
+#ifndef KSQUIRREL_PART
+ if(!reset_mode)
+ SQ_WidgetStack::instance()->lastFile();
+#endif
+}
+
+void SQ_GLWidget::slotNext()
+{
+#ifndef KSQUIRREL_PART
+ timer_next->stop();
+ timer_prev->stop();
+
+ timer_next->start(timer_delay_file, true);
+#endif
+}
+
+void SQ_GLWidget::slotPrev()
+{
+#ifndef KSQUIRREL_PART
+ timer_prev->stop();
+ timer_next->stop();
+
+ timer_prev->start(timer_delay_file, true);
+#endif
+}
+
+void SQ_GLWidget::slotZoomMenu()
+{
+ zoomMenu->exec(TQCursor::pos());
+}
+
+void SQ_GLWidget::slotAnimateNext()
+{
+ // Some time ago we started to decode new image, but it is
+ // not guaranteed that animation stopped!
+ //
+ // We should return now to avoid segfaults...
+ if(reset_mode)
+ return;
+
+// tab->parts[tab->current].removeParts();
+
+ tab->current++;
+
+ if(tab->current >= (int)tab->finfo.image.size())
+ tab->current = 0;
+
+ updateCurrentFileInfo();
+ updateGL();
+
+ int delay = tab->finfo.image[tab->current].delay;
+
+ timer_anim->start(delay, true);
+}
+
+void SQ_GLWidget::startAnimation()
+{
+ if(!tab->finfo.animated) return;
+
+ timer_anim->start(tab->finfo.image[tab->current].delay, true);
+}
+
+void SQ_GLWidget::stopAnimation()
+{
+ timer_anim->stop();
+}
+
+void SQ_GLWidget::slotToggleAnimate()
+{
+ if(!tab->finfo.animated || gls->valid()) return;
+
+ if(!timer_anim->isActive())
+ {
+ tab->manualBlocked = false;
+ startAnimation();
+ }
+ else
+ {
+ tab->manualBlocked = true;
+ stopAnimation();
+ }
+}
+
+/*
+ * Next image in animated sequence. Called by user (with F3).
+ */
+void SQ_GLWidget::nextImage()
+{
+ // if the image has only one page - do nothing
+ if(tab->total == 1)
+ return;
+
+ tab->current++;
+
+ if(tab->current >= tab->total)
+ tab->current = 0;
+
+ updateGL();
+ updateCurrentFileInfo();
+}
+
+/*
+ * Previous image in animated sequence. Called by user (with F2).
+ */
+void SQ_GLWidget::prevImage()
+{
+ if(tab->total == 1)
+ return;
+
+ tab->current--;
+
+ if(tab->current < 0)
+ tab->current = tab->total - 1;
+
+ updateGL();
+ updateCurrentFileInfo();
+}
+
+/*
+ * Jump to first/last image page.
+ */
+void SQ_GLWidget::jumpToImage(bool last)
+{
+ // if the image has only one page - do nothing
+ if(tab->total == 1)
+ return;
+
+ tab->current = (last) ? tab->finfo.image.size() - 1 : 0;
+
+ updateGL();
+ updateCurrentFileInfo();
+}
+
+void SQ_GLWidget::initMarks()
+{
+ for(int i = 0;i < 4;i++)
+ {
+ glGenTextures(1, &mark[i]);
+
+ glBindTexture(GL_TEXTURE_2D, mark[i]);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, mm[i].bits());
+ }
+}
+
+void SQ_GLWidget::internalZoom(const GLfloat &zF)
+{
+ tab->curangle = 0.0;
+
+ oldZoom = getZoom();
+ matrix_pure_reset();
+
+ exifRotate(false);
+
+ matrix_zoom(zF);
+}
+
+void SQ_GLWidget::updateFilter(bool nice)
+{
+ if(nice == linear)
+ return;
+
+ // store
+ linear = nice;
+
+ int filter = nice ? GL_LINEAR : GL_NEAREST;
+ Parts *pt;
+
+ // update all textures
+ for(int i = 0;i < tab->total;i++)
+ {
+ pt = tab->broken ? parts_broken : &tab->parts[i];
+ int toxy = pt->m_parts.size();
+
+ for(int j = 0;j < toxy;j++)
+ {
+ glBindTexture(GL_TEXTURE_2D, pt->m_parts[j].tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ }
+ }
+
+ updateGL();
+}
+
+/*
+ * Cleanup method.
+ */
+void SQ_GLWidget::decodeFailedOn0(const int err_code)
+{
+ tab->codeK->read_close();
+ tab->finfo.image.clear();
+ tab->finfo.meta.clear();
+ tab->total = 0;
+ decoded = (bool)tabs.size();
+ reset_mode = false;
+ tab->broken = true;
+ tab->lib = 0;
+
+ useBrokenImage(err_code);
+}
+
+/*
+ * Force using broken image + update context.
+ * Show appropriate error message in statusbar.
+ */
+void SQ_GLWidget::useBrokenImage(const int err_index)
+{
+ enableSettingsButton(false);
+ enableActions(false);
+
+ // save "broken" image information in 'tab->finfo'
+ tab->finfo.image.push_back(image_broken);
+
+ // reset statusbar widgets
+ SQ_GLView::window()->resetStatusBar();
+
+#ifndef KSQUIRREL_PART
+ // show error message instead of file name
+ SQ_GLView::window()->sbarWidget("SBFile")->setText(SQ_ErrorString::instance()->string(err_index));
+ KSquirrel::app()->setCaption(TQString());
+#else
+ t_glv.sbarWidget("SBFile")->setText(SQ_ErrorString::instance()->string(err_index));
+#endif
+
+ matrix_pure_reset();
+ tab->curangle = 0;
+ tab->isflippedH = tab->isflippedV = false;
+
+ changeSlider(1.0);
+
+ // update context and show "broken" image
+ updateGL();
+}
+
+/*
+ * Are we in fullscreen state ?
+ */
+bool SQ_GLWidget::fullscreen() const
+{
+#ifndef KSQUIRREL_PART
+ KWin::WindowInfo wi = KWin::windowInfo(SQ_GLView::window()->winId());
+
+ if(wi.valid())
+ pAFull->setChecked((wi.state() & NET::FullScreen));
+
+ return pAFull->isChecked();
+#else
+ return false;
+#endif
+}
+
+/*
+ * Toggle fullscreen state.
+ */
+void SQ_GLWidget::toggleFullScreen()
+{
+#ifndef KSQUIRREL_PART
+ bool fs = !fullscreen();
+
+ pAFull->setChecked(fs);
+ pAToolFull->setOn(fs);
+
+ KSquirrel::app()->slotFullScreen(fs);
+#endif
+}
+
+void SQ_GLWidget::slotSetZoomPercents(int perc)
+{
+ if(tab->broken || tab->finfo.image.empty()) return;
+
+ GLfloat z = (perc <= 20) ? (GLfloat)perc / 20 : ((GLfloat)perc - 20)/2 + 1.0;
+
+ internalZoom(z);
+}
+
+void SQ_GLWidget::updateFactors()
+{
+ zoomfactor = SQ_Config::instance()->readNumEntry("zoom", 25);
+ movefactor = SQ_Config::instance()->readNumEntry("move", 5);
+ rotatefactor = SQ_Config::instance()->readNumEntry("angle", 90);
+}
+
+void SQ_GLWidget::slotSelectionRect()
+{
+ stopAnimation();
+ tab->glselection = SQ_GLSelectionPainter::Rectangle;
+ gls->end();
+}
+
+void SQ_GLWidget::slotSelectionEllipse()
+{
+ stopAnimation();
+ tab->glselection = SQ_GLSelectionPainter::Ellipse;
+ gls->end();
+}
+
+void SQ_GLWidget::slotSelectionClear()
+{
+ tab->glselection = -1;
+ gls->end();
+
+ pASelectionEllipse->setChecked(false);
+ pASelectionRect->setChecked(false);
+
+ if(!manualBlocked())
+ startAnimation();
+}
+
+bool SQ_GLWidget::manualBlocked()
+{
+ // selection is also blocks animation
+ return tab->manualBlocked || gls->valid();
+}
+
+void SQ_GLWidget::setDownloadPercents(int p)
+{
+ if(p < 0)
+ percentsLabel->hide();
+ else
+ {
+ percentsLabel->setText(i18n("Downloading...") + ' ' + TDEIO::convertSize(p));
+ percentsLabel->adjustSize();
+ percentsLabel->show();
+ }
+}
+
+#include "sq_glwidget.moc"