diff options
Diffstat (limited to 'src/sq_glwidget.cpp')
-rw-r--r-- | src/sq_glwidget.cpp | 2006 |
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" |