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.
tdegraphics/kfax/kfax.cpp

1697 lines
37 KiB

/*
$Id$
Copyright (C) 1997 Bernd Johannes Wuebben
wuebben@math.cornell.edu
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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef KDE_USE_FINAL
/* NewImage() in viewfax.cpp needs to fiddle with the Display structure */
#define XLIB_ILLEGAL_ACCESS
#endif
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <tqfile.h>
#include <tqstrlist.h>
#include <tqtimer.h>
#include <tqpopupmenu.h>
#include <tqpainter.h>
#include <tqpaintdevicemetrics.h>
#include <tqbitmap.h>
#include <klocale.h>
#include <kaboutdata.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <kfiledialog.h>
#include <kfilemetainfo.h>
#include <kstdaccel.h>
#include <kconfig.h>
#include <kmenubar.h>
#include <kmessagebox.h>
#include <kcmdlineargs.h>
#include <kio/netaccess.h>
#include <knotifyclient.h>
#include <ktempfile.h>
#include <kstdaction.h>
#include <kdebug.h>
#include <kurldrag.h>
#include <kstatusbar.h>
#include <kaction.h>
#include <kprocess.h>
#include <kprinter.h>
#include <kio/job.h>
#include <kdebug.h>
#include "faxexpand.h"
#include "kfax.h"
#include "version.h"
#include "viewfax.h"
#include "options.h"
#include "kfax_printsettings.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/XKBlib.h>
#include <X11/keysym.h>
#include <X11/keysymdef.h>
#include <X11/cursorfont.h>
// StatusBar field IDs
#define ID_LINE_COLUMN 1
#define ID_INS_OVR 2
#define ID_GENERAL 3
#define ID_FNAME 4
#define ID_TYPE 5
#define ID_PAGE_NO 6
TopLevel *toplevel;
extern int GetImage(struct pagenode *pn);
void TurnFollowing(int How, struct pagenode *pn);
void handle_X_event(XEvent event);
void ShowLoop(void);
void SetupDisplay();
void mysighandler(int sig);
void setFaxDefaults();
void parse(char* buf, char** args);
extern void g31expand(struct pagenode *pn, drawfunc df);
extern void g32expand(struct pagenode *pn, drawfunc df);
extern void g4expand(struct pagenode *pn, drawfunc df);
#define PATIENCE 100000
static char KFAX_FILETYPES[] = "image/fax-g3 image/tiff";
int ExpectConfNotify = 1;
GC PaintGC;
Cursor WorkCursor;
Cursor ReadyCursor;
Cursor MoveCursor;
Cursor LRCursor;
Cursor UDCursor;
extern Time Lasttime;
extern bool have_cmd_opt;
extern struct pagenode *viewpage;
extern XImage *FlipImage(XImage *xi);
extern XImage *MirrorImage(XImage *xi);
extern XImage *RotImage(XImage *Image);
extern XImage *ZoomImage(XImage *Big);
extern void FreeImage(XImage *Image);
extern XImage *Image, *Images[MAXZOOM];
int xpos, ox; /* x, old x, offset x, new xpos*/
int ypos, oy; /* y, old y, offset y, new y */
int offx, offy;
int nx, ny;
int oz, Resize, Refresh; /* old zoom, window size changed,
needs updating */
int PaneWidth, PaneHeight; /* current size of our window */
int AbsX, AbsY; /* absolute position of centre of window */
Display* qtdisplay;
int startingup;
Window qtwin; // the qt window
Window Win;
int qwindow_height;
int qwindow_width;
bool have_no_fax = TRUE;
bool display_is_setup = FALSE;
struct optionsinfo fop; // contains the fax options
extern struct pagenode *firstpage, *lastpage, *thispage;
extern struct pagenode* auxpage;
extern struct pagenode defaultpage;
bool buttondown;
bool MyApp::x11EventFilter( XEvent * ev)
{
if (KApplication::x11EventFilter(ev))
return TRUE;
if (ev->type == ButtonRelease){
/* this is so that the cursor goes back to normal on leaving the fax window
and that the fax won't be moved when I reenter after I release the mouse*/
if (buttondown == true){
buttondown = false;
XDefineCursor(qtdisplay, Win, ReadyCursor);
XFlush(qtdisplay);
}
}
if ( ev->xany.window == qtwin ||
ev->xany.window == Win){
if(startingup || have_no_fax)
return FALSE;
toplevel->handle_X_event(*ev);
ev->xany.window = qtwin;
}
return FALSE;
}
TopLevel::TopLevel (TQWidget *, const char *name)
: KMainWindow (0, name)
{
setMinimumSize (100, 100);
buttondown = false;
setupMenuBar();
setupStatusBar();
updateActions();
resize(550,400);
setupGUI();
readSettings();
faxqtwin = new TQFrame(this);
qtwin = faxqtwin->winId();
faxqtwin->setFrameStyle(TQFrame::Panel | TQFrame::Sunken);
// Create aQt::Vertical scroll bar
vsb = new TQScrollBar( Qt::Vertical,faxqtwin,"scrollBar" );
vsb->hide();
connect( vsb, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(scrollVert(int)) );
// Create aQt::Horizontal scroll bar
hsb = new TQScrollBar( Qt::Horizontal,faxqtwin,"scrollBar" );
connect( hsb, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(scrollHorz(int)) );
hsb->hide();
setCentralWidget(faxqtwin);
setAcceptDrops(true);
show();
}
TopLevel::~TopLevel()
{
}
void TopLevel::setupMenuBar()
{
// File menu
KStdAction::open( TQT_TQOBJECT(this), TQT_SLOT( faxOpen() ), actionCollection() );
actRecent = KStdAction::openRecent( TQT_TQOBJECT(this), TQT_SLOT( faxOpen( const KURL & ) ),
actionCollection() );
actSave = KStdAction::save( TQT_TQOBJECT(this), TQT_SLOT( faxSave() ), actionCollection() );
actSaveAs = KStdAction::saveAs( TQT_TQOBJECT(this), TQT_SLOT( faxSaveAs() ),
actionCollection() );
actPrint = KStdAction::print( TQT_TQOBJECT(this), TQT_SLOT( print() ), actionCollection() );
KStdAction::quit( TQT_TQOBJECT(this), TQT_SLOT( close() ), actionCollection() );
actAdd = new KAction( i18n( "A&dd..." ), "filenew", KShortcut(), TQT_TQOBJECT(this),
TQT_SLOT( faxAdd() ), actionCollection(), "file_add_fax" );
actRecent->setMaxItems( 5 );
// View Menu
actSize = KStdAction::actualSize( TQT_TQOBJECT(this), TQT_SLOT( actualSize() ),
actionCollection() );
actZoomIn = KStdAction::zoomIn( TQT_TQOBJECT(this), TQT_SLOT( zoomin() ), actionCollection() );
actZoomOut = KStdAction::zoomOut( TQT_TQOBJECT(this), TQT_SLOT( zoomout() ),
actionCollection() );
actRotate = new KAction( i18n( "&Rotate Page" ), "rotate", KShortcut(), TQT_TQOBJECT(this),
TQT_SLOT( rotatePage() ), actionCollection(), "view_rotate" );
actMirror = new KAction( i18n( "Mirror Page" ), KShortcut(), TQT_TQOBJECT(this),
TQT_SLOT( mirrorPage() ), actionCollection(), "view_mirror" );
actFlip = new KAction( i18n( "&Flip Page" ), KShortcut(), TQT_TQOBJECT(this),
TQT_SLOT( flipPage() ), actionCollection(), "view_flip" );
// Go menu
actNext = KStdAction::next( TQT_TQOBJECT(this), TQT_SLOT( nextPage() ), actionCollection() );
actPrev = KStdAction::prior( TQT_TQOBJECT(this), TQT_SLOT( prevPage() ), actionCollection() );
actFirst = KStdAction::firstPage( TQT_TQOBJECT(this), TQT_SLOT( firstPage() ),
actionCollection() );
actLast = KStdAction::lastPage( TQT_TQOBJECT(this), TQT_SLOT( lastPage() ),
actionCollection() );
// Settings menu
KStdAction::preferences( TQT_TQOBJECT(this), TQT_SLOT( faxoptions() ), actionCollection() );
}
void TopLevel::setupStatusBar()
{
statusbar = statusBar();
statusbar->insertFixedItem(i18n("w: 00000 h: 00000"), ID_INS_OVR);
statusbar->insertFixedItem(i18n("Res: XXXXX"), ID_GENERAL);
statusbar->insertFixedItem(i18n("Type: XXXXXXX"), ID_TYPE);
statusbar->insertFixedItem(i18n("Page: XX of XX"), ID_PAGE_NO);
statusbar->insertItem(TQString(), ID_FNAME, 1);
statusbar->setItemAlignment( ID_FNAME, AlignLeft );
statusbar->changeItem(TQString(), ID_INS_OVR);
statusbar->changeItem(TQString(), ID_GENERAL);
statusbar->changeItem(TQString(), ID_TYPE);
statusbar->changeItem(TQString(), ID_PAGE_NO);
statusbar->changeItem(TQString(), ID_FNAME);
}
void TopLevel::readSettings()
{
config = kapp->config();
applyMainWindowSettings( config, "MainWindowSettings" );
actRecent->loadEntries( config );
config->setGroup("General Options");
config->setGroup("Fax Options");
fop.width = config->readNumEntry("width", 1728);
fop.resauto = config->readNumEntry("resauto", 1);
fop.geomauto = config->readNumEntry("geomauto", 1);
fop.height = config->readNumEntry("height", 2339);
fop.fine = config->readNumEntry("resolution", 1);
fop.flip = config->readNumEntry("flip", 0);
fop.invert = config->readNumEntry("invert", 0);
fop.lsbfirst = config->readNumEntry("lsb", 0);
fop.raw = config->readNumEntry("raw", 3);
setFaxDefaults();
}
void TopLevel::updateActions()
{
actAdd->setEnabled( thispage );
actSave->setEnabled( thispage );
actSaveAs->setEnabled( thispage );
actPrint->setEnabled( thispage );
actRotate->setEnabled( thispage );
actFlip->setEnabled( thispage );
actMirror->setEnabled( thispage );
updateGoActions();
updateZoomActions();
}
void TopLevel::updateGoActions()
{
actNext->setEnabled( thispage && thispage->next );
actPrev->setEnabled( thispage && thispage->prev );
actFirst->setEnabled( thispage && thispage->prev );
actLast->setEnabled( thispage && thispage->next );
}
void TopLevel::updateZoomActions()
{
actSize->setEnabled( Image && oz > 0 );
actZoomIn->setEnabled( Image && oz > 0 );
actZoomOut->setEnabled( Image && oz < MAXZOOM-1 && Image->width >= 256 );
}
bool TopLevel::queryClose()
{
saveMainWindowSettings( config, "MainWindowSettings" );
actRecent->saveEntries( config );
return true;
}
void TopLevel::writeSettings()
{
config = kapp->config();
config->setGroup("General Options");
config->setGroup("Fax Options");
config->writeEntry("resauto",fop.resauto);
config->writeEntry("geomauto",fop.geomauto);
config->writeEntry("width",fop.width);
config->writeEntry("height",fop.height);
config->writeEntry("resolution",fop.fine);
config->writeEntry("flip",fop.flip);
config->writeEntry("invert",fop.invert);
config->writeEntry("lsb",fop.lsbfirst);
config->writeEntry("raw",fop.raw);
config->sync();
}
void TopLevel::faxOpen()
{
KURL url = KFileDialog::getOpenURL(TQString(), KFAX_FILETYPES);
faxOpen( url );
actRecent->addURL( fileURL );
}
void TopLevel::faxOpen( const KURL & url )
{
if (!url.isValid())
return;
faxClose();
faxAdd( url );
fileURL = url;
updateActions();
}
void TopLevel::faxAdd()
{
KURL url = KFileDialog::getOpenURL(TQString(), KFAX_FILETYPES);
faxAdd( url );
actRecent->addURL( fileURL );
}
void TopLevel::faxAdd( const KURL & url )
{
if (!url.isValid())
return;
openNetFile(url);
updateGoActions();
}
void TopLevel::faxSave()
{
saveNetFile(fileURL);
}
void TopLevel::faxSaveAs()
{
fileURL = KFileDialog::getSaveURL(TQString(), KFAX_FILETYPES);
if (fileURL.isEmpty())
return;
faxSave();
actRecent->addURL( fileURL );
}
static void freeImages()
{
int i;
for (i = 1; i < MAXZOOM; i++) {
if (Images[i])
FreeImage(Images[i]);
Images[i] = NULL;
}
}
static XImage *generateZoomImages(int maxzoom)
{
int i;
for (i = 1; i < MAXZOOM; i++){
if (!Images[i-1])
continue;
Image = Images[i] = ZoomImage(Images[i-1]);
if(Image == NULL){// out of memory
Image = Images[i -1];
break;
}
}
i = maxzoom;
while (!Images[i])
i--;
return Images[i];
}
void TopLevel::zoom( int factor )
{
if(!thispage || !Image || !faxqtwin || !display_is_setup)
return;
Resize = Refresh = 1;
Image = generateZoomImages(factor);
PaneWidth = Image->width;
PaneHeight = Image->height;
resizeView();
putImage();
uiUpdate();
updateZoomActions();
}
void TopLevel::zoomin()
{
if ( oz > 0 )
{
oz--;
zoom( oz );
}
}
void TopLevel::zoomout()
{
if (oz < MAXZOOM && Image->width >= 256)
{
++oz;
zoom( oz );
}
}
void TopLevel::actualSize()
{
oz = 0;
zoom( oz );
}
void loadfile(TQString filename)
{
// Typical FAX resolutions are:
// Standard: 203 dpi x 98 dpi
// Fine: 203 dpi x 196 lpi
// Super Fine: 203 dpi y 392 lpi, or
// 406 dpi x 392 lpi
TQSize dpi(203,196);
KFileMetaInfo metaInfo(filename);
if (metaInfo.isValid() && metaInfo.item("Resolution").isValid())
{
TQSize s = metaInfo.item("Resolution").value().toSize();
if (s.width() >= 100 && s.height() >= 100)
dpi = s;
}
(void) notetiff(TQFile::encodeName(filename));
struct pagenode *pn;
for(pn = firstpage; pn; pn = pn->next)
if (!pn->dpiX) {
pn->dpiX = dpi.width();
pn->dpiY = dpi.height();
}
}
void TopLevel::openadd(TQString filename)
{
auxpage = lastpage;
loadfile(filename);
if( firstpage != lastpage )
{
if(auxpage->next)
auxpage = auxpage->next;
}
// auxpage should now point to the first pagenode which was created for
// the newly added fax file.
have_no_fax = false;
thispage = auxpage;
newPage();
resizeView();
putImage();
}
void TopLevel::resizeEvent(TQResizeEvent *e)
{
KMainWindow::resizeEvent(e);
resizeView();
}
void TopLevel::wheelEvent( TQWheelEvent *e )
{
e->accept();
if ( e->state() & ShiftButton )
{
if ( e->delta() < 0 )
zoomin();
else
zoomout();
}
else
{
int offset = TQApplication::wheelScrollLines()*vsb->lineStep();
if ( e->state() & ControlButton )
offset = vsb->pageStep();
offset = -e->delta()*offset/120;
vsb->setValue( vsb->value() + offset );
}
}
void TopLevel::resizeView()
{
if(!faxqtwin || !display_is_setup)
return;
//printf("In resizeView() entered\n");
qwindow_width = faxqtwin->width();
qwindow_height = faxqtwin->height();
if( hsb->isVisible())
qwindow_height -= 16;
if( vsb->isVisible())
qwindow_width -= 16;
if(Image){
PaneWidth = Image->width;
PaneHeight = Image->height;
}
// printf("faxw %d qtw %d\n", PaneWidth , faxqtwin->width());
if( (PaneHeight > qwindow_height ) &&
(PaneWidth > qwindow_width)){
vsb->setGeometry(faxqtwin->width() - 16,0,16,faxqtwin->height()-16);
hsb->setGeometry(0,faxqtwin->height() - 16 ,faxqtwin->width()-16,16);
qwindow_width = faxqtwin->width() -16;
qwindow_height = faxqtwin->height()-16;
vsb->raise();
vsb->show();
hsb->show();
}
else{
if( PaneHeight > qwindow_height){
vsb->setGeometry(faxqtwin->width() - 16,0,16,faxqtwin->height());
qwindow_width = faxqtwin->width() -16 ;
qwindow_height = faxqtwin->height();
vsb->show();
hsb->hide();
hsb->raise();
}
else
vsb->hide();
if( PaneWidth > qwindow_width ){
hsb->setGeometry(0,faxqtwin->height() - 16 ,faxqtwin->width(),16);
hsb->show();
hsb->raise();
vsb->hide();
qwindow_width = faxqtwin->width() ;
qwindow_height = faxqtwin->height() -16;
}
else
hsb->hide();
}
if(Image){
hsb->setRange(0,TQMAX(0,Image->width - qwindow_width));
hsb->setSteps(5,qwindow_width/2);
// printf("hsb range: %d\n",TQMAX(0,Image->width - qwindow_width));
vsb->setRange(0,TQMAX(0,Image->height - qwindow_height));
vsb->setSteps(5,qwindow_height/2);
// printf("vsb range: %d\n",TQMAX(0,Image->height - qwindow_height));
// printf("vsb TQMIN %d vdb TQMAX %d\n",vsb->TQMINValue(),vsb->TQMAXValue());
}
Resize = 1;
uiUpdate();
}
bool TopLevel::loadAllPages( int &numpages, int &currentpage )
{
struct pagenode *pn;
numpages = 0;
currentpage = 1;
for(pn = firstpage; pn; pn = pn->next) {
++numpages;
if (pn == thispage)
currentpage = numpages;
if (!Pimage(pn)) {
int k = -1;
while((k != 0) && (k != 3) && (k != 1))
k = GetImage(pn); // fetch image if it is not available yet.
}
}
return (numpages != 0);
}
void TopLevel::print(){
if(!thispage || !firstpage) {
return KMessageBox::sorry(this, i18n("There is no document active."));
}
int pages, currentpage;
loadAllPages(pages, currentpage);
KPrinter printer(true, TQPrinter::HighResolution);
printer.setFullPage( true );
printer.setUsePrinterResolution( true );
printer.setCreator( i18n("KFax") + " " KFAXVERSION );
printer.setDocName( TQString("%1 - %2").arg(firstpage->name).arg(i18n("KFax")));
printer.setDocFileName( firstpage->name );
printer.setPageSelection( KPrinter::ApplicationSide );
printer.setMinMax( 1, pages );
printer.setCurrentPage( currentpage );
printer.addDialogPage(new KFAXPrintSettings());
if ( !printer.setup( this ) )
return;
TQPainter painter;
painter.begin( &printer );
printIt(printer, painter);
painter.end();
}
void TopLevel::printIt( KPrinter &printer, TQPainter &painter )
{
TQPaintDeviceMetrics dm(painter.device());
TQApplication::setOverrideCursor( waitCursor );
kapp->processEvents();
const bool fullpage = printer.option(APP_KFAX_SCALE_FULLPAGE) == "true";
const bool center_h = printer.option(APP_KFAX_CENTER_HORZ) == "true";
const bool center_v = printer.option(APP_KFAX_CENTER_VERT) == "true";
int currentpage = 0;
bool first_page_printed = false;
struct pagenode *pn;
for(pn = firstpage; pn; pn = pn->next) {
++currentpage;
// should this page be printed ?
if (printer.pageList().findIndex(currentpage) < 0)
continue;
XImage *Image = Pimage(pn);
if (!Image)
continue;
// byte-swapping the image
TQByteArray bytes( Image->height*Image->bytes_per_line );
for (int y=Image->height-1; y>=0; --y) {
TQ_UINT32 offset = y*Image->bytes_per_line;
TQ_UINT32 *source = (TQ_UINT32 *) (Image->data + offset);
TQ_UINT32 *dest = (TQ_UINT32 *) (bytes.data() + offset);
for (int x=(Image->bytes_per_line/4)-1; x>=0; --x) {
TQ_UINT32 dv = 0, sv = *source;
for (int bit=32; bit>0; --bit) {
dv <<= 1;
dv |= sv&1;
sv >>= 1;
}
*dest = dv;
++dest;
++source;
}
}
TQImage image( (uchar *)bytes.data(), Image->bytes_per_line*8, Image->height, 1, NULL, 2, TQImage::LittleEndian);
if (first_page_printed)
printer.newPage();
first_page_printed = true;
const TQSize printersize( dm.width(), dm.height() );
kdDebug() << "Printersize = " << printersize << endl;
// print Image in original size if possible, else scale it.
const TQSize size( // logical size of the image
Image->width * dm.logicalDpiX() / pn->dpiX,
Image->height * dm.logicalDpiY() / pn->dpiY
);
kdDebug() << "Org image size = " << Image->width << "x" << Image->height
<< " logical picture res = " << pn->dpiX << "x" << pn->dpiY << endl;
kdDebug() << "New image size = " << size
<< " logical printer res = " << dm.logicalDpiX() << "x" << dm.logicalDpiY() << endl;
uint top, left, bottom, right;
if (fullpage)
top = left = bottom = right = 0;
else
printer.margins( &top, &left, &bottom, &right );
kdDebug() << "Margins = " << top << " " << left << " " << bottom << " " << right << endl;
const TQSize maxSize( printersize.width()-left-right, printersize.height()-top-bottom );
TQSize scaledImageSize = size;
if (size.width() > maxSize.width() || size.height() > maxSize.height() ) {
// Image does not fit - scale it and print centered
scaledImageSize.scale( maxSize, TQSize::ScaleMin );
kdDebug() << "Image does not fit - scaling to " << maxSize << endl;
} else {
// Image does fit - print it in original size, but centered
scaledImageSize.scale( size, TQSize::ScaleMin );
kdDebug() << "Image does fit - scaling to " << size << endl;
}
kdDebug() << "Final image size " << scaledImageSize << endl;
int x,y;
if (center_h)
x = (maxSize.width()-scaledImageSize.width())/2 + left;
else
x = left;
if (center_v)
y = (maxSize.height()-scaledImageSize.height())/2 + top;
else
y = top;
painter.drawImage( TQRect(x,y,scaledImageSize.width(), scaledImageSize.height()), image );
}
TQApplication::restoreOverrideCursor();
}
void TopLevel::saveNetFile( const KURL& dest)
{
if ( !dest.isValid() )
{
KMessageBox::sorry(this, i18n("Malformed URL"));
return;
}
statusbar->message( i18n( "Saving..." ) );
KURL source = KURL::fromPathOrURL(thispage->pathname);
bool ok = KIO::NetAccess::file_copy( source, dest, -1, true, false, this);
statusbar->clear();
if (!ok)
KMessageBox::error(this, i18n("Failure in 'copy file()'\n"
"Could not save file!"));
}
void TopLevel::openNetFile( const KURL &u)
{
if ( !u.isValid() )
{
KMessageBox::error(this, i18n("Malformed URL"));
return;
}
if ( u.isLocalFile() )
{
TQString string = i18n("Loading '%1'").arg(u.path());
statusbar->message(string);
openadd( u.path());
statusbar->clear();
}
else
{
statusbar->message(i18n("Downloading..."));
TQString tmpFile = TQString();
if ( KIO::NetAccess::download( u, tmpFile, this ) )
{
openadd( tmpFile );
setCaption( u.prettyURL() );
}
statusbar->clear();
KIO::NetAccess::removeTempFile( tmpFile );
}
}
void TopLevel::dragEnterEvent( TQDragEnterEvent * event)
{
event->accept(KURLDrag::canDecode(event));
}
void TopLevel::dropEvent( TQDropEvent * event)
{
KURL::List list;
if (KURLDrag::decode(event, list) && !list.isEmpty())
{
// Load the first file in this window
const KURL &url = list.first();
openNetFile( url );
}
}
void SetupDisplay(){
if(display_is_setup){
return;
}
display_is_setup = TRUE;
xpos = ypos = ox = oy = 0;
ExpectConfNotify = 1;
/* XSizeHints size_hints;*/
Win = XCreateSimpleWindow(qtdisplay,qtwin,1,1,
1,1,
0,
BlackPixel(qtdisplay,XDefaultScreen(qtdisplay)),
WhitePixel(qtdisplay,XDefaultScreen(qtdisplay)));
PaintGC = XCreateGC(qtdisplay, Win, 0L, (XGCValues *) NULL);
XSetForeground(qtdisplay, PaintGC, BlackPixel(qtdisplay, XDefaultScreen(qtdisplay) ));
XSetBackground(qtdisplay, PaintGC, WhitePixel(qtdisplay, XDefaultScreen(qtdisplay) ));
XSetFunction(qtdisplay, PaintGC, GXcopy);
WorkCursor = XCreateFontCursor(qtdisplay, XC_watch);
//ReadyCursor = XCreateFontCursor(qtdisplay, XC_plus);
ReadyCursor = XCreateFontCursor(qtdisplay, XC_hand2);
MoveCursor = XCreateFontCursor(qtdisplay, XC_fleur);
LRCursor = XCreateFontCursor(qtdisplay, XC_sb_h_double_arrow);
UDCursor = XCreateFontCursor(qtdisplay, XC_sb_v_double_arrow);
XSelectInput(qtdisplay, Win, Button2MotionMask | ButtonPressMask |
ButtonReleaseMask | ExposureMask | KeyPressMask |
SubstructureNotifyMask | LeaveWindowMask | OwnerGrabButtonMask |
StructureNotifyMask);
XMapRaised(qtdisplay, Win);
XDefineCursor(qtdisplay, Win, ReadyCursor);
XFlush(qtdisplay);
memset(Images, 0, sizeof(Images));
// Start at half the Size
oz = 1;
}
void TopLevel::handle_X_event(XEvent Event)
{
if(!thispage || !Image || !faxqtwin || !display_is_setup)
return;
bool putimage = false; // Do we actually have to write the image to the scree?
do {
switch(Event.type) {
case MappingNotify:
XRefreshKeyboardMapping((XMappingEvent *)(&Event));
break;
case LeaveNotify:
/* buttondown = false;
XDefineCursor(qtdisplay, Win, ReadyCursor);
XFlush(qtdisplay);*/
break;
case Expose:
{
if(Event.xexpose.count != 0)
break;
if(!Image)
break;
putimage = TRUE;
}
break;
case KeyPress:
if (ExpectConfNotify &&
(Event.xkey.time < (Lasttime + PATIENCE)))
break;
Lasttime = Event.xkey.time;
ExpectConfNotify = 0;
switch(XkbKeycodeToKeysym(qtdisplay, Event.xkey.keycode, 0, 0)) {
case XK_m:
mirrorPage();
if (Event.xkey.state & ShiftMask)
TurnFollowing(TURN_M, thispage->next);
break;
case XK_o:
zoomout();
break;
case XK_i:
zoomin();
break;
case XK_Up:
ypos-= qwindow_height / 3;
putimage = TRUE;
break;
case XK_Down:
ypos+= qwindow_height / 3;
putimage = TRUE;
break;
case XK_Left:
xpos-= qwindow_width / 4;
putimage = TRUE;
break;
case XK_Right:
xpos+= qwindow_width / 4;
putimage = TRUE;
break;
case XK_Home:
case XK_R7:
if (Event.xkey.state & ShiftMask) {
thispage = firstpage;
newPage();
resizeView();
putImage();
break;
}
xpos= 0;
ypos= 0;
putImage();
break;
case XK_End:
case XK_R13:
if (Event.xkey.state & ShiftMask) {
thispage = lastpage;
newPage();
resizeView();
putImage();
break;
}
xpos= Image->width;
ypos= Image->height;
putImage();
break;
case XK_l:
case XK_r:
rotatePage();
if (Event.xkey.state & ShiftMask)
TurnFollowing(TURN_L, thispage->next);
break;
case XK_p:
case XK_minus:
case XK_Prior:
case XK_R9:
case XK_BackSpace:
prevPage();
break;
case XK_n:
case XK_plus:
case XK_space:
case XK_Next:
case XK_R15:
nextPage();
break;
case XK_u:
flipPage();
if (Event.xkey.state & ShiftMask)
TurnFollowing(TURN_U, thispage->next);
break;
case XK_q:
if (viewpage) {
thispage = viewpage;
viewpage = NULL;
newPage();
resizeView();
putImage();
}
}
break;
case ButtonPress:
if (ExpectConfNotify && (Event.xbutton.time < (Lasttime + PATIENCE)))
break;
Lasttime = Event.xbutton.time;
ExpectConfNotify = 0;
switch (Event.xbutton.button) {
case Button1:
buttondown = true;
switch (((Image->width > qwindow_width)<<1) |
(Image->height > qwindow_height)) {
case 0:
break;
case 1:
XDefineCursor(qtdisplay, Win, UDCursor);
break;
case 2:
XDefineCursor(qtdisplay, Win, LRCursor);
break;
case 3:
XDefineCursor(qtdisplay, Win, MoveCursor);
}
XFlush(qtdisplay);
offx = Event.xbutton.x;
offy = Event.xbutton.y;
break;
}
break;
case MotionNotify:
if(!buttondown)
break;
do {
nx = Event.xmotion.x;
ny = Event.xmotion.y;
} while (XCheckTypedEvent(qtdisplay, MotionNotify, &Event));
xpos+= offx - nx;
ypos+= offy - ny;
offx = nx;
offy = ny;
putimage = TRUE;
break;
case ButtonRelease:
if (Event.xbutton.button == Button1) {
buttondown = false;
XDefineCursor(qtdisplay, Win, ReadyCursor);
XFlush(qtdisplay);
}
}
} while (XCheckWindowEvent(qtdisplay, Win, KeyPressMask|ButtonPressMask, &Event));
if(putimage == TRUE) {
Refresh = Resize = 1;
putImage();
}
}
void TopLevel::rotatePage()
{
if(!thispage || !Image || !faxqtwin || !display_is_setup)
return;
XDefineCursor(qtdisplay, Win, WorkCursor);
XFlush(qtdisplay);
XImage *newrotimage = RotImage(Images[0]);
XDefineCursor(qtdisplay, Win, ReadyCursor);
if(newrotimage == NULL){ // out of memory
return;
}
thispage->extra = Images[0] = newrotimage;
thispage->orient ^= TURN_L;
freeImages();
Image = generateZoomImages(oz);
{ int t = xpos ; xpos= ypos; ypos= t; }
Refresh = Resize = 1;
putImage();
}
void TopLevel::flipPage()
{
if(!thispage || !Image || !faxqtwin || !display_is_setup)
return;
XDefineCursor(qtdisplay, Win, WorkCursor);
XFlush(qtdisplay);
XImage *newflipimage = FlipImage(Images[0]);
XDefineCursor(qtdisplay, Win, ReadyCursor);
if(newflipimage == NULL){ // out of memory
return;
}
thispage->extra = Images[0] = newflipimage;
thispage->orient ^= TURN_U;
freeImages();
Image = generateZoomImages(oz);
Refresh = Resize = 1;
putImage();
}
void TopLevel::mirrorPage()
{
if(!thispage || !Image || !faxqtwin || !display_is_setup)
return;
XDefineCursor(qtdisplay, Win, WorkCursor);
XFlush(qtdisplay);
XImage *newmirror = MirrorImage(Images[0]);
XDefineCursor(qtdisplay, Win, ReadyCursor);
if(newmirror == NULL){ // out of memory
return;
}
thispage->extra = Images[0] = newmirror;
thispage->orient ^= TURN_M;
freeImages();
Image = generateZoomImages(oz);
Refresh = Resize = 1;
putImage();
}
void TopLevel::scrollHorz(int){
if(!Image)
return;
// printf("hsb value: %d\n",hsb->value());
xpos= hsb->value() + qwindow_width/2;
Refresh = 1;
putImage();
}
void TopLevel::scrollVert(int ){
if(!Image)
return;
// printf("vsb value: %d\n",vsb->value());
ypos= vsb->value() + qwindow_height/2;
Refresh = 1;
putImage();
}
void TopLevel::lastPage()
{
if(!thispage)
return;
if ( thispage->next )
{
while(thispage->next != NULL)
thispage = thispage->next;
newPage();
resizeView();
putImage();
}
updateGoActions();
}
void TopLevel::firstPage()
{
if(!thispage)
return;
if ( thispage->prev )
{
while(thispage->prev != NULL)
thispage = thispage->prev;
newPage();
resizeView();
putImage();
}
updateGoActions();
}
void TopLevel::nextPage()
{
if(!thispage)
return;
if (thispage->next)
{
thispage = thispage->next;
newPage();
resizeView();
putImage();
}
updateGoActions();
}
void TopLevel::prevPage()
{
if(!thispage)
return;
if (thispage->prev)
{
thispage = thispage->prev;
newPage();
resizeView();
putImage();
}
updateGoActions();
}
void TopLevel::newPage(){
if(!display_is_setup)
SetupDisplay();
XDefineCursor(qtdisplay, Win, WorkCursor);
XFlush(qtdisplay);
freeImages();
int k = -1;
if(!thispage) {
XDefineCursor(qtdisplay, Win, ReadyCursor);
return;
}
if (Pimage(thispage) == NULL){
while((k != 0) && (k != 3) && (k !=1))
k = GetImage(thispage);
}
if (k == 3 ){
XDefineCursor(qtdisplay, Win, ReadyCursor);
FreeFax();
/* KMessageBox::sorry(i18n("Bad fax file k=3"));*/
return;
}
if (k == 0 ){
XDefineCursor(qtdisplay, Win, ReadyCursor);
FreeFax();
/* KMessageBox::sorry(i18n("Bad fax file k=0"));*/
return;
}
Image = Images[0] = Pimage(thispage);
setCaption(TQFile::decodeName(thispage->name));
Image = generateZoomImages(oz);
PaneWidth = Image->width;
PaneHeight = Image->height;
Refresh = 1;
XDefineCursor(qtdisplay, Win, ReadyCursor);
uiUpdate();
}
void TopLevel::faxClose()
{
FreeFax();
setCaption(i18n("KFax"));
// TODO replace this with unmapping the window.
if(display_is_setup)
XResizeWindow(qtdisplay,Win,1,1); // we want a clear gray background.
resizeView();
vsb->hide();
hsb->hide();
fileURL = TQString();
updateActions();
}
void TopLevel::FreeFax()
{
if(display_is_setup)
XClearWindow(qtdisplay, Win);
freeImages();
pagenode *pn;
for (pn = firstpage; pn; pn = pn->next){
if(Pimage(pn)){
FreeImage(Pimage(pn));
pn->extra = NULL;
}
}
Image = NULL;
for (pn = firstpage; pn; pn = pn->next){
if(pn->pathname){
free(pn->pathname);
}
}
if(firstpage){
for(pn = firstpage->next; pn; pn = pn->next){
if(pn->prev){
free(pn->prev);
}
}
}
if(lastpage)
free(lastpage);
firstpage = lastpage = viewpage = thispage = auxpage = NULL;
uiUpdate();
}
void TopLevel::uiUpdate(){
if(thispage){
struct pagenode *pn ;
int pages = 0;
int currentpage = 0;
for(pn = firstpage; pn ; pn = pn->next){
pages ++;
if (thispage == pn)
currentpage = pages;
}
TQString pagestr = i18n("Page: %1 of %2").arg(currentpage).arg(pages);
statusbar->changeItem(pagestr, ID_PAGE_NO);
if(Image){
TQString wh = i18n("W: %1 H: %2").arg(Image->width).arg(Image->height);
statusbar->changeItem(wh, ID_INS_OVR);
}
TQString resolution = i18n("Res: %1").arg(thispage->vres?i18n("Fine"):i18n("Normal"));
// TODO: resolution += TQString("%1x%2").arg(thispage->dpiX).arg(thispage->dpiY);
statusbar->changeItem(resolution, ID_GENERAL);
statusbar->changeItem(thispage->name, ID_FNAME);
TQString typestring;
if(thispage->type == FAX_TIFF){
typestring = i18n("Type: Tiff ");
}
else if ( thispage->type == FAX_RAW){
typestring = i18n("Type: Raw ");
}
if ( thispage->expander == g31expand )
typestring += "G3";
if ( thispage->expander == g32expand )
typestring += "G3 2D";
if ( thispage->expander == g4expand )
typestring += "G4";
statusbar->changeItem(typestring,ID_TYPE);
updateActions();
}
}
void kfaxerror(const TQString& title, const TQString& error){
KMessageBox::error(toplevel, error, title);
}
void TopLevel::putImage()
{
// TODO do I really need to set Refresh or Resize to 1 , is there
// really still someonce calling this with out haveing set Refresh or Resize to 1?
if ( !Image || !display_is_setup || !thispage )
return;
if ( qwindow_width > Image->width){
xpos= Image->width/2;
}
else{
if(xpos< qwindow_width/2){
xpos = qwindow_width/2;
}
else{
if(xpos> Image->width - qwindow_width/2){
xpos= Image->width - qwindow_width/2;
}
}
}
if ( qwindow_height > Image->height){
ypos= Image->height/2;
}
else{
if(ypos< qwindow_height/2){
ypos = qwindow_height/2;
}
else{
if(ypos> Image->height - qwindow_height/2){
ypos= Image->height - qwindow_height/2;
}
}
}
if (xpos!= ox || ypos!= oy || Refresh || Resize){
/* In the following we use qwindow_height -1 etc since the main view
has a sunken frame and I need to offset by 1 pixel to the right and
one pixel down so that I don't paint my fax into the border of the frame*/
XResizeWindow(qtdisplay,Win,TQMIN(qwindow_width -1,Image->width ),
TQMIN(qwindow_height -1,Image->height ));
XPutImage(qtdisplay, Win, PaintGC, Image,
TQMAX(xpos - qwindow_width/2,0), TQMAX(ypos - qwindow_height/2,0),
0, 0, TQMIN(qwindow_width -1,Image->width) ,
TQMIN(qwindow_height -1,Image->height) );
vsb->setValue(TQMAX(ypos - qwindow_height/2,0));
hsb->setValue(TQMAX(xpos - qwindow_width/2,0));
XFlush(qtdisplay);
}
ox = xpos;
oy = ypos;
Resize = Refresh = 0;
}
void TopLevel::faxoptions(){
OptionsDialog * opd = new OptionsDialog(this, "options");
opd->setWidgets(&fop);
if(opd->exec()){
struct optionsinfo *newops;
newops = opd->getInfo();
fop.resauto = newops->resauto;
fop.geomauto = newops->geomauto;
fop.width = newops->width;
fop.height = newops->height;
fop.fine = newops->fine;
fop.landscape= newops->landscape;
fop.flip = newops->flip;
fop.invert = newops->invert;
fop.lsbfirst = newops->lsbfirst;
fop.raw = newops->raw;
setFaxDefaults();
writeSettings();
}
delete opd;
}
void setFaxDefaults(){
// fop is called in readSettings, so this can't be
// called after a TopLevel::readSettings()
if(have_cmd_opt ) // we have commad line options all kfaxrc defaults are
return; // overridden
if(fop.resauto == 1)
defaultpage.vres = -1;
else
defaultpage.vres = fop.fine;
if(fop.geomauto == 1){
defaultpage.width = defaultpage.height = 0;
}
else{
defaultpage.width = fop.width;
defaultpage.height = fop.height;
}
if(fop.landscape)
defaultpage.orient |= TURN_L;
if(fop.flip)
defaultpage.orient |= TURN_U;
defaultpage.inverse = fop.invert;
defaultpage.lsbfirst = fop.lsbfirst;
switch (fop.raw) {
case 2: defaultpage.expander = g32expand;
break;
case 4: defaultpage.expander = g4expand;
break;
default:defaultpage.expander = g31expand;
}
}
static const char description[] =
I18N_NOOP("TDE G3/G4 Fax Viewer");
static KCmdLineOptions options[] =
{
{"f", 0, 0 },
{"fine", I18N_NOOP( "Fine resolution" ), 0 },
{"n", 0, 0 },
{"normal", I18N_NOOP( "Normal resolution" ), 0 },
{"height", I18N_NOOP( "Height (number of fax lines)" ), 0 },
{"w", 0, 0 },
{"width", I18N_NOOP( "Width (dots per fax line)" ), 0 },
{"l", 0, 0 },
{"landscape", I18N_NOOP( "Turn image 90 degrees (landscape mode)" ), 0 },
{"u", 0, 0 },
{"upsidedown", I18N_NOOP( "Turn image upside down" ), 0 },
{"i", 0, 0 },
{"invert", I18N_NOOP( "Invert black and white" ), 0 },
{"m", 0, 0 },
{"mem <bytes>", I18N_NOOP( "Limit memory use to 'bytes'" ), "8M" },
{"r", 0, 0 },
{"reverse", I18N_NOOP( "Fax data is packed lsb first" ), 0 },
{"2" , I18N_NOOP( "Raw files are g3-2d" ), 0 },
{"4", I18N_NOOP( "Raw files are g4" ), 0 },
{"+file(s)", I18N_NOOP( "Fax file(s) to show" ), 0 },
KCmdLineLastOption
};
int main (int argc, char **argv)
{
KAboutData aboutData( "kfax", I18N_NOOP("KFax"),
KFAXVERSION, description, KAboutData::License_GPL,
"(c) 1997-98 Bernd Johannes Wuebben");
aboutData.addAuthor( "Bernd Johannes Wuebben", 0, "wuebben@kde.org" );
aboutData.addCredit( "Nadeem Hasan", I18N_NOOP( "UI Rewrite, lots of code "
"cleanups and fixes" ), "nhasan@kde.org" );
aboutData.addCredit( "Helge Deller", I18N_NOOP( "Printing Rewrite, lots of code "
"cleanups and fixes"), "deller@kde.org" );
KCmdLineArgs::init(argc, argv, &aboutData);
KCmdLineArgs::addCmdLineOptions( options );
MyApp a;
qtdisplay = tqt_xdisplay();
viewfaxmain();
toplevel = new TopLevel();
toplevel->show();
startingup = 1;
a.processEvents();
a.flushX();
startingup = 0;
faxinit();
if(!have_no_fax){
thispage = firstpage;
toplevel->newPage();
toplevel->resizeView();
//TODO : I don't think I need this putImage();
toplevel->putImage();
}
toplevel->uiUpdate();
return a.exec ();
}
#include "kfax.moc"