You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ksquirrel/ksquirrel/sq_glwidget.cpp

2007 lines
50 KiB

/***************************************************************************
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"