diff --git a/eimage/eimage.cpp b/eimage/eimage.cpp index d616912..3e75fe9 100644 --- a/eimage/eimage.cpp +++ b/eimage/eimage.cpp @@ -1,76 +1,72 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include +#include -using namespace fltk; +#define DEBUG 1 -// Automatic detection of image type still doesn't work in fltk :( -static struct { - const char* filename; - SharedImage* (*func)(const char*, const uchar*); -} supported[] = { - { "*.png", pngImage::get}, - {"*.jpg", jpegImage::get}, - {"*.gif", gifImage::get}, - {"*.bmp", bmpImage::get}, - {"*.xpm", xpmFileImage::get}, - {0, 0} -}; +// Supported image types +const char* supported[] = {"bm","bmp","gif","jpg","pbm","pgm","png","ppm","xbm","xpm",0}; - -// Making these global vastly simplifies the code -// cause they're used a lot -// The proper thing to do is to make the whole program one big class -char filename[PATH_MAX], directory[PATH_MAX]; -Window* w; -class CenteredInScroll; -CenteredInScroll* c; +// Global variables used everywhere +char filename[FL_PATH_MAX], directory[FL_PATH_MAX]; +Fl_Window* w; float zoomfactor; bool autozoom=false; +Fl_Shared_Image *im; -// Variables used in nextpic & prevpic +class ScrolledImage; +ScrolledImage* s; + + +// Directory list cache used in prevnext() dirent **files; int nfiles; -// Forward declaration of funcs for use from event handler +// Forward declaration of funcs for use from callbacks void nextpic(); void prevpic(); void loadimage(); void newdir(); -// Callbacks for popup menu -void next_cb(Widget*) { nextpic(); } -void prev_cb(Widget*) { prevpic(); } -void open_cb(Widget*) { - const char* f = file_chooser("Choose image or directory","Image Files (*.{bmp,gif,jpg,png,xpm})",directory); + +// Callbacks for main menu +void next_cb(Fl_Widget*,void*) { nextpic(); } +void prev_cb(Fl_Widget*,void*) { prevpic(); } +void open_cb(Fl_Widget*,void*) { + // construct filename filter + char filter[FL_PATH_MAX]; + snprintf(filter, FL_PATH_MAX, "Image files(*.{%s", supported[0]); + for (int i=1; supported[i]; i++) + snprintf(filter, FL_PATH_MAX, "%s,%s", filter, supported[i]); + snprintf(filter, FL_PATH_MAX, "%s})", filter); + + const char* f = fl_file_chooser("Choose image or directory",filter,directory); if (!f) return; - strncpy(filename,f,PATH_MAX); + + strncpy(filename,f,FL_PATH_MAX); newdir(); loadimage(); } -void manage_cb(Widget* b) {} // call file manager -void fullscr_cb(Widget* b) { + +void manage_cb(Fl_Widget* b,void*) {} // call file manager +void fullscreen_cb(Fl_Widget* b,void*) { static bool isfull=false; static int X,Y,W,H; if (isfull) { @@ -82,136 +78,116 @@ void fullscr_cb(Widget* b) { isfull=true; } } -void exit_cb(Widget* b) { exit(0); } -void zoomin_cb(Widget* b) { +void zoomin_cb(Fl_Widget* b,void*) { if (zoomfactor>=1) zoomfactor += 0.2; else zoomfactor += zoomfactor/5; autozoom=false; loadimage(); } -void zoomout_cb(Widget* b) { +void zoomout_cb(Fl_Widget* b,void*) { if (zoomfactor>=1) zoomfactor -= 0.2; else zoomfactor -= zoomfactor/5; autozoom=false; loadimage(); } -void zoomrestore_cb(Widget* b) { zoomfactor = 1; autozoom=false; loadimage(); } -void zoomauto_cb(Widget *b) { autozoom = !autozoom; loadimage(); } +void zoomrestore_cb(Fl_Widget* b,void*) { zoomfactor = 1; autozoom=false; loadimage(); } +void zoomauto_cb(Fl_Widget *b,void*) { autozoom = !autozoom; loadimage(); } +void about_cb(Fl_Widget* b,void*) {} // about window +void exit_cb(Fl_Widget* b,void*) { exit(0); } + + + +// Main popup menu +Fl_Menu_Item mainmenu[] = { + {"&Open", FL_CTRL+'o', open_cb}, + {"&Manage", 0, manage_cb, 0, FL_MENU_DIVIDER}, + + {"&Previous", FL_Page_Up, prev_cb}, + {"&Next", FL_Page_Down, next_cb, 0, FL_MENU_DIVIDER}, + + {"&Zoom in", '+', zoomin_cb}, + {"Zoom &out", '-', zoomout_cb}, + {"Zoom &auto", FL_CTRL+'a', zoomauto_cb}, + {"&Restore", '/', zoomrestore_cb, 0, FL_MENU_DIVIDER}, + + {"&Fullscreen", FL_F+11, fullscreen_cb, 0, FL_MENU_DIVIDER}, + + {"A&bout", 0, about_cb}, + {"&Exit", FL_Escape, exit_cb}, + {0} +}; -class CenteredInScroll : public Button { +class ScrolledImage : public Fl_Scroll { private: - PopupMenu* popup; + Fl_Box* b; + Fl_Menu_Button* mb; + public: - CenteredInScroll(int x, int y, int w, int h, const char*l = 0) - : Button(x,y,w,h,l) { - popup = new PopupMenu(0, 0, 0, 0); - if(popup->parent()) - popup->parent()->remove(popup); - popup->parent(0); - popup->type(PopupMenu::POPUP3); - popup->begin(); + ScrolledImage(int x, int y, int w, int h, const char*l = 0) + : Fl_Scroll(x,y,w,h,l) { + align(FL_ALIGN_INSIDE|FL_ALIGN_CENTER); + begin(); + + b = new Fl_Box(w/2,h/2,0,0); - {Item *i = new Item("&Open..."); - i->callback(open_cb); - i->shortcut(CTRL+'o'); + mb = new Fl_Menu_Button (0,0,0,0,""); + mb->type(Fl_Menu_Button::POPUP3); + mb->menu(0); + + end(); + redraw(); + } + + void image(Fl_Image* a) { b->image(a); } + void image(Fl_Image& a) { b->image(a); } + Fl_Image* image() { return b->image(); } + void label(const char* a) { + if (a) resizebox(0,0); // center label + b->label(a); + } + + // Resize the box containing image + void resizebox(int W, int H) { + int X=0,Y=0; + int aw = w()-scrollbar.w(); int ah=h()-hscrollbar.h(); + if(aw>W) X=(aw-W)/2; + if(ah>H) Y=(ah-H)/2; + b->resize(X,Y,W,H); } - {Item *i = new Item("&Manage..."); - i->callback(manage_cb); + void resize(int x,int y,int w,int h) { + Fl_Scroll::resize(x,y,w,h); + resizebox(b->w(),b->h()); // refresh image position + redraw(); } - new Divider(); - - {Item *i = new Item("&Next"); - i->callback(next_cb); - i->shortcut(DownKey); + virtual int handle(int event) { + if (event == FL_PUSH) { + if(Fl::event_button()==3 && mb->menu()!=0) { + mb->popup(); + return 1; + } + } + // sometimes PgUp and PgDown aren't handled + else if (event == FL_SHORTCUT && mb->menu()!=0) { + int key = Fl::event_key(); + if (key==FL_Page_Up || key==FL_Page_Down) + return mb->handle(event); + } + return Fl_Scroll::handle(event); } - {Item *i = new Item("&Previous"); - i->callback(prev_cb); - i->shortcut(UpKey); - } - - new Divider(); - - {Item *i = new Item("&Zoom in"); - i->callback(zoomin_cb); - i->shortcut('+'); - } - - {Item *i = new Item("Zoom &out"); - i->callback(zoomout_cb); - i->shortcut('-'); - } - - {Item *i = new Item("Zoom &auto"); - i->callback(zoomauto_cb); - i->shortcut(CTRL+'a'); - } - - {Item *i = new Item("&Restore"); - i->callback(zoomrestore_cb); - i->shortcut('/'); - } - - new Divider(); - - {Item *i = new Item("&Fullscreen"); - i->callback(fullscr_cb); - i->shortcut(F11Key); - } - - new Divider(); - - {Item *i = new Item("A&bout"); - i->callback(exit_cb); - } - - {Item *i = new Item("&Exit"); - i->callback(exit_cb); - i->shortcut(EscapeKey); - } - - - - popup->end(); - } - - virtual void draw() { - int ix, iy; - if (w() < parent()->w()) { ix=((parent()->w() - w())/2); } else { ix=0; } - if (h() < parent()->h()) { iy=((parent()->h() - h())/2); } else { iy=0; } - Image *i = (Image*)image(); - i->draw(Rectangle(ix,iy,w(),h())); - } - - virtual int handle(int event) { - if (event == KEY) { - int key = event_key(); - const char* text = event_text(); // needed to detect some keys - if (key == LeftKey || key == UpKey) prevpic(); - else if (key == RightKey || key == DownKey) nextpic(); - // Shortcuts from popup menu wont work unless we define them here - // The 'this' is just to pass something to callback, it wont be used - else if (key == CTRL+'o') open_cb(this); - else if (strcmp(text,"+")==0 || key == AddKey) zoomin_cb(this); - else if (strcmp(text,"-")==0 || key == SubtractKey) zoomout_cb(this); - else if (strcmp(text,"/")==0 || key == DivideKey) zoomrestore_cb(this); - else if (key == CTRL+'a') zoomauto_cb(this); - else if (key == F11Key) fullscr_cb(this); - } - else if (event == PUSH) { - if(event_button()==3) { - popup->popup(); - return 1; + void setmenu(Fl_Menu_Item* menu) { + if (menu!=0) { + mb->menu(menu); } } - return Button::handle(event); - } }; + + // Directory changed, get new directory from filename void newdir() { int p=0; @@ -227,66 +203,114 @@ void newdir() { // Load the image given in char[] filename void loadimage() { - fprintf(stderr, "Loadimage() - file: %s\n",filename); + char tmp[FL_PATH_MAX]; // the string buffer + + if (DEBUG) fprintf(stderr, "Loadimage() - file: %s\n",filename); // Load image - SharedImage *im; - for (int j=0; supported[j].filename; j++) - if (filename_match(filename,supported[j].filename)) { - im = supported[j].func(filename,0); - break; - } + if (im) { im->release(); im=0; } + im = Fl_Shared_Image::get(filename); // image type is autodetected now - int W,H; - im->measure(W,H); // Should this wait until image is loaded? - if (autozoom) { - // Adjust zoom factor so picture fits on screen - // When switch to manual zooming, this factor will be used - float pw=c->parent()->w()+1, ph=c->parent()->h()+1; - // use float to avoid rounding - if (pw/W < ph/H) zoomfactor=pw/W; else zoomfactor=ph/H; + if (!im) { + if (DEBUG) fprintf(stderr, "Fl_Shared_Image::get() failed!\n"); + s->image(0); + snprintf(tmp, FL_PATH_MAX, "Can't load image %s\n",filename); + s->label(strdup(tmp)); + s->redraw(); + return; } - c->w((int)W*zoomfactor-1); // cast to int and -1 help avoid - c->h((int)H*zoomfactor-1); // scrollbars when autozoom is on - c->image(im); + // Measure image + int realw=im->w(), realh=im->h(); + int scaledw,scaledh; - c->parent()->relayout(); // remove scrollbars if necessary - c->parent()->redraw(); // remove traces of old picture + if (autozoom) { + // Adjust zoom factor so picture fits inside window + // When user switches to manual zooming, this factor will remain + float fw=(float)s->w()/realw; float fh=(float)s->h()/realh; + if (fw < fh) zoomfactor=fw; else zoomfactor=fh; + } - c->label(""); // clear any previous labels + // Resample image to new size + scaledw=realw*zoomfactor; + scaledh=realh*zoomfactor; - char tmp[PATH_MAX]; // set window title + if (zoomfactor!=1) { + Fl_Image *temp = im->copy(scaledw,scaledh); + im = (Fl_Shared_Image*) temp; + } + + // Set image + s->resizebox(scaledw,scaledh); + s->image(im); + + s->label(0); // clear any previous labels + s->redraw(); + + // set window title if (zoomfactor==1) - snprintf(tmp,PATH_MAX,"%s (%dx%d) - View picture",filename_name(filename),W,H); + snprintf(tmp,FL_PATH_MAX,"%s (%dx%d) - View picture",fl_filename_name(filename),realw,realh); else - snprintf(tmp,PATH_MAX,"%s (%dx%d) - zoom %1.1fx - View picture",filename_name(filename),W,H,zoomfactor); + snprintf(tmp,FL_PATH_MAX,"%s (%dx%d) - zoom %1.1fx - View picture",fl_filename_name(filename),realw,realh,zoomfactor); w->label(strdup(tmp)); } -// Get next/previous picture file in directory d -// (universal func. to be called from nextpic() and prevpic() +// Get next/previous picture file in directory +// (universal func. to be called from nextpic() and prevpic() ) void prevnext(int direction) { - fprintf(stderr, "Nextpic() - file: %s dir: %s direction: %d\n",filename,directory,direction); + char tmp[FL_PATH_MAX]; // the string buffer + + if (DEBUG) + fprintf(stderr, "Prevnext() - file: %s dir: %s direction: %d\n",filename,directory,direction); if (nfiles == 0) { // read directory - nfiles = filename_list(directory,&files); + nfiles = fl_filename_list(directory,&files); } // Select next picture after current bool found=false; - const char* justname = filename_name(filename); - // this basically means: if direction is 1 go from first to last, else from last to first - for (int i=(direction?0:nfiles-1); (direction?i=0); i+=(direction?1:-1)) { - if (strncmp(justname,files[i]->d_name,PATH_MAX) == 0) { - found=true; - continue; + if (filename[0]) { + const char* justname = fl_filename_name(filename); + + // this basically means: if direction is 1 go from first to last, else from last to first + for (int i=(direction?0:nfiles-1); (direction?i=0); i+=(direction?1:-1)) { + if (strncmp(justname,files[i]->d_name,FL_PATH_MAX) == 0) { + found=true; + continue; // skip to next file + } + if (found) { + for (int j=0; supported[j]; j++) { + snprintf(tmp,FL_PATH_MAX,"*.%s",supported[j]); + if (fl_filename_match(files[i]->d_name,tmp)) { + snprintf(filename,FL_PATH_MAX,"%s/%s",directory,files[i]->d_name); + loadimage(); + return; + } + } + } } - if (found) { - for (int j=0; supported[j].filename; j++) { - if (filename_match(files[i]->d_name,supported[j].filename)) { - snprintf(filename,PATH_MAX,"%s/%s",directory,files[i]->d_name); + } + + if (found) { //this means that the current picture is the last/first in directory + if (im) { im->release(); im=0; } + s->image(0); + filename[0]=0; + + if (direction) + s->label("This was the last picture.\nPress 'Next' again for first one."); + else + s->label("This was the first picture.\nPress 'Previous' again for last one."); + s->redraw(); + return; + + } else { + // Just give first (or last) picture in directory + for (int i=(direction?0:nfiles-1); (direction?i=0); i+=(direction?1:-1)) { + for (int j=0; supported[j]; j++) { + snprintf(tmp,FL_PATH_MAX,"*.%s",supported[j]); + if (fl_filename_match(files[i]->d_name,tmp)) { + snprintf(filename,FL_PATH_MAX,"%s/%s",directory,files[i]->d_name); loadimage(); return; } @@ -294,31 +318,18 @@ void prevnext(int direction) { } } - // Current picture not found, give first in directory - for (int i=(direction?0:nfiles-1); (direction?i=0); i+=(direction?1:-1)) { - for (int j=0; supported[j].filename; j++) { - if (filename_match(files[i]->d_name,supported[j].filename)) { - snprintf(filename,PATH_MAX,"%s/%s",directory,files[i]->d_name); - loadimage(); - return; - } - } - } - // Nothing found... - fprintf(stderr, "Nextpic() - nothing found\n"); - char tmp[PATH_MAX]; - snprintf(tmp,PATH_MAX,"No pictures in directory %s",directory); - c->label(strdup(tmp)); - // Position label on center and redraw everything - c->w(1); c->h(1); - c->x(c->parent()->w()/2); - c->y(c->parent()->h()/2); - c->redraw(); - c->parent()->relayout(); - c->parent()->redraw(); + if (DEBUG) fprintf(stderr, "Nextpic() - nothing found\n"); + + if (im) { im->release(); im=0; } + s->image(0); + filename[0]=0; + snprintf(tmp,FL_PATH_MAX,"No pictures in directory %s",directory); + s->label(strdup(tmp)); + s->redraw(); + // Window title - snprintf(tmp,PATH_MAX,"View picture - nothing found in %s",directory); + snprintf(tmp,FL_PATH_MAX,"View picture - nothing found in %s",directory); w->label(strdup(tmp)); } @@ -328,59 +339,47 @@ void prevpic() { prevnext(0); } int main (int argc, char **argv) { - filename[0]='\0'; directory[0]='\0'; zoomfactor=1; + filename[0]='\0'; directory[0]='\0'; zoomfactor=1; im=0; + fl_register_images(); - w = new Window(200, 200, "View picture"); - w->set_vertical(); - w->set_double_buffer(); - w->begin(); - {ScrollGroup* g = new ScrollGroup(0, 0, 200, 200); - //g->set_vertical(); -// Group::current()->resizable(o); - g->box(FLAT_BOX); - g->color(GRAY05); -// g->color(WHITE); - g->align(ALIGN_RIGHT); - g->resize_align(ALIGN_RIGHT); - g->begin(); - {c = new CenteredInScroll(0,0,200,200); - g->align(ALIGN_LEFT|ALIGN_TOP); - c->box(NO_BOX); - c->focusbox(NO_BOX); - g->color(GRAY05); - c->labelcolor(WHITE); - c->labelsize(14); - c->tooltip("Right click for menu"); - c->take_focus(); - } - g->end(); - } + + // Main window + + w = new Fl_Window(200, 200, "View picture"); + s = new ScrolledImage(0,0,200,200); + s->color(33); + s->labelcolor(FL_WHITE); + s->setmenu(mainmenu); + w->resizable(s); w->end(); - w->resizable(w); + w->align(FL_ALIGN_INSIDE|FL_ALIGN_CENTER); + + // Analyze command line if (argc==1) { // No params - strncpy (directory, getenv("HOME"), PATH_MAX); + strncpy (directory, getenv("HOME"), FL_PATH_MAX); nextpic(); - } else if (filename_isdir(argv[1])) { // Param is directory - strncpy (directory, argv[1], PATH_MAX); + } else if (fl_filename_isdir(argv[1])) { // Param is directory + strncpy (directory, argv[1], FL_PATH_MAX); nextpic(); argc--; argv++; // ignore this param and forward rest to fltk } else { // Param is file if (argv[1][0] == '~' && argv[1][1] == '/') // expand home dir - snprintf (filename, PATH_MAX, "%s/%s", getenv("HOME"), argv[1]+2); + snprintf (filename, FL_PATH_MAX, "%s/%s", getenv("HOME"), argv[1]+2); else if (argv[1][0] != '/') // relative filename - snprintf (filename, PATH_MAX, "%s/%s", getenv("PWD"), argv[1]); + snprintf (filename, FL_PATH_MAX, "%s/%s", getenv("PWD"), argv[1]); else // absolute filename - strncpy (filename, argv[1], PATH_MAX); + strncpy (filename, argv[1], FL_PATH_MAX); - if (!filename_exist(argv[1])) { - char tmp[PATH_MAX]; - snprintf(tmp,PATH_MAX,"File not found - %s",filename); - c->label(tmp); + struct stat last_stat; // Does file exist? + if (stat(argv[0], &last_stat)!=0) { + char tmp[FL_PATH_MAX]; + snprintf(tmp,FL_PATH_MAX,"File not found - %s",filename); + s->label(tmp); } else loadimage(); @@ -388,7 +387,14 @@ int main (int argc, char **argv) { argc--; argv++; // ignore this param and forward rest to fltk } + // Resize window to image size or screen + int W,H; + if (im->w()>Fl::w()) W=Fl::w(); else W=im->w(); + if (im->h()>Fl::h()) H=Fl::h(); else H=im->h(); + w->resize(0,0,W,H); + // Window manager should make sure that window is fully visible + w->show(argc,argv); - return run(); + return Fl::run(); }