/* * figinset.C - part of LyX project */ /* Rework of path-handling (Matthias 04.07.1996 ) * ------------------------------------------------ * figinsets keep an absolute path to the eps-file. * For the user alway a relative path appears * (in lyx-file, latex-file and edit-popup). * To calculate this relative path the path to the * document where the figinset is in is needed. * This is done by a reference to the buffer, called * figinset::cbuffer. To be up to date the cbuffer * is sometimes set to the current buffer * bufferlist.current(), when it can be assumed that * this operation happens in the current buffer. * This is true for InsetFig::Edit(...), * InsetFig::InsetFig(...), InsetFig::Read(...), * InsetFig::Write and InsetFig::Latex(...). * Therefore the bufferlist has to make sure that * during these operations bufferlist.current() * returns the buffer where the figinsets are in. * This made few changes in buffer.C necessary. * * The above is not totally valid anymore. (Lgb) */ #include #include #include #include #include #include #include #include #include #include FORMS_H_LOCATION #include #include #include #include "figinset.h" #include "lyx.h" #include "lyx_main.h" #include "buffer.h" #include "filedlg.h" #include "support/filetools.h" #include "LyXView.h" // just because of form_main #include "debug.h" #include "LaTeXFeatures.h" #include "lyxrc.h" #include "gettext.h" #include "lyx_gui_misc.h" // CancelCloseBoxCB #include "support/FileInfo.h" #include "support/lyxlib.h" #include "Painter.h" #include "font.h" #include "bufferview_funcs.h" #include "ColorHandler.h" #include "converter.h" using std::ostream; using std::istream; using std::ofstream; using std::ifstream; using std::queue; using std::list; using std::vector; using std::find; using std::flush; using std::endl; using std::ostringstream; using std::copy; extern BufferView * current_view; extern FL_OBJECT * figinset_canvas; extern char ** environ; // is this only redundtant on linux systems? Lgb. static float const DEG2PI = 57.295779513; struct queue_element { float rx, ry; // resolution x and y int ofsx, ofsy; // x and y translation figdata * data; // we are doing it for this data }; static int const MAXGS = 3; /* maximum 3 gs's at a time */ typedef vector figures_type; typedef vector bitmaps_type; static figures_type figures; // all figures static bitmaps_type bitmaps; // all bitmaps static queue gsqueue; // queue for ghostscripting static int gsrunning = 0; /* currently so many gs's are running */ static bool bitmap_waiting = false; /* bitmaps are waiting finished */ static bool gs_color; // do we allocate colors for gs? static bool color_visual; // is the visual color? static bool gs_xcolor = false; // allocated extended colors static unsigned long gs_pixels[128]; // allocated pixels static int gs_spc; // shades per color static int gs_allcolors; // number of all colors static list pidwaitlist; // pid wait list static GC createGC() { XGCValues val; val.foreground = BlackPixel(fl_get_display(), DefaultScreen(fl_get_display())); val.function=GXcopy; val.graphics_exposures = false; val.line_style = LineSolid; val.line_width = 0; return XCreateGC(fl_get_display(), RootWindow(fl_get_display(), 0), GCForeground | GCFunction | GCGraphicsExposures | GCLineWidth | GCLineStyle , &val); } static GC local_gc_copy; static void addpidwait(int pid) { // adds pid to pid wait list pidwaitlist.push_back(pid); if (lyxerr.debugging()) { lyxerr << "Pids to wait for: \n"; copy(pidwaitlist.begin(), pidwaitlist.end(), std::ostream_iterator(lyxerr, "\n")); lyxerr << flush; } } static string make_tmp(int pid) { return system_tempdir + "/~lyxgs" + tostr(pid) + ".ps"; } static void kill_gs(int pid, int sig) { if (lyxerr.debugging()) lyxerr << "Killing gs " << pid << endl; lyx::kill(pid, sig); lyx::unlink(make_tmp(pid)); } extern "C" // static int GhostscriptMsg(FL_OBJECT *, Window, int, int, XEvent * ev, void *) { XClientMessageEvent * e = reinterpret_cast(ev); if(lyxerr.debugging()) { lyxerr << "ClientMessage, win:[xx] gs:[" << e->data.l[0] << "] pm:[" << e->data.l[1] << "]" << endl; } // just kill gs, that way it will work for sure // This loop looks like S**T so it probably is... for (bitmaps_type::iterator it = bitmaps.begin(); it != bitmaps.end(); ++it) if (static_cast((*it)->bitmap) == static_cast(e->data.l[1])) { // found the one figdata * p = (*it); p->gsdone = true; // first update p->bitmap, if necessary if (p->bitmap != None && p->flags > (1|8) && gs_color && p->wid) { // query current colormap and re-render // the pixmap with proper colors XWindowAttributes wa; register XImage * im; int i; int y; int spc1 = gs_spc - 1; int spc2 = gs_spc * gs_spc; int wid = p->wid; int forkstat; Display * tmpdisp; GC gc = local_gc_copy; XGetWindowAttributes(fl_get_display(), fl_get_canvas_id( figinset_canvas), &wa); XFlush(fl_get_display()); if (lyxerr.debugging()) { lyxerr << "Starting image translation " << p->bitmap << " " << p->flags << " " << p->wid << "x" << p->hgh << " " << wa.depth << " " << XYPixmap << endl; } // now fork rendering process forkstat = fork(); if (forkstat == -1) { lyxerr.debug() << "Cannot fork, using slow " "method for pixmap translation." << endl; tmpdisp = fl_get_display(); } else if (forkstat > 0) { // parent // register child if (lyxerr.debugging()) { lyxerr << "Spawned child " << forkstat << endl; } addpidwait(forkstat); break; } else { // child tmpdisp = XOpenDisplay(XDisplayName(0)); XFlush(tmpdisp); } im = XGetImage(tmpdisp, p->bitmap, 0, 0, p->wid, p->hgh, (1<hgh; ++y) { for (int x = 0; x < wid; ++x) { XColor * pc = cmap + XGetPixel(im, x, y); XFlush(tmpdisp); XPutPixel(im, x, y, gs_pixels[((pc->red+6553)* spc1/65535)*spc2+((pc->green+6553)* spc1/65535)*gs_spc+((pc->blue+6553)* spc1/65535)]); XFlush(tmpdisp); } } // This must be correct. delete [] cmap; if (lyxerr.debugging()) { lyxerr << "Putting image back" << endl; } XPutImage(tmpdisp, p->bitmap, gc, im, 0, 0, 0, 0, p->wid, p->hgh); XDestroyImage(im); if (lyxerr.debugging()) { lyxerr << "Done translation" << endl; } } noim: kill_gs(p->gspid, SIGHUP); if (forkstat == 0) { XCloseDisplay(tmpdisp); _exit(0); } } else { kill_gs(p->gspid, SIGHUP); } break; } return 0; } static void AllocColors(int num) // allocate color cube numxnumxnum, if possible { if (lyxerr.debugging()) { lyxerr << "Allocating color cube " << num << 'x' << num << 'x' << num << endl; } if (num <= 1) { lyxerr << "Error allocating color colormap." << endl; gs_color = false; return; } if (num > 5) num = 5; XColor xcol; for (int i = 0; i < num * num * num; ++i) { xcol.red = short(65535 * (i / (num * num)) / (num - 1)); xcol.green = short(65535 * ((i / num) % num) / (num - 1)); xcol.blue = short(65535 * (i % num) / (num - 1)); xcol.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(fl_get_display(), fl_state[fl_get_vclass()].colormap, &xcol)) { if (i) XFreeColors(fl_get_display(), fl_state[fl_get_vclass()].colormap, gs_pixels, i, 0); if(lyxerr.debugging()) { lyxerr << "Cannot allocate color cube " << num << endl;; } AllocColors(num - 1); return; } gs_pixels[i] = xcol.pixel; } gs_color = true; gs_spc = num; } // allocate grayscale ramp static void AllocGrays(int num) { if (lyxerr.debugging()) { lyxerr << "Allocating grayscale colormap " << num << endl; } if (num < 4) { lyxerr << "Error allocating grayscale colormap." << endl; gs_color = false; return; } if (num > 128) num = 128; XColor xcol; for (int i = 0; i < num; ++i) { xcol.red = xcol.green = xcol.blue = short(65535 * i / (num - 1)); xcol.flags = DoRed | DoGreen | DoBlue; if (!XAllocColor(fl_get_display(), fl_state[fl_get_vclass()].colormap, &xcol)) { if (i) XFreeColors(fl_get_display(), fl_state[fl_get_vclass()].colormap, gs_pixels, i, 0); if (lyxerr.debugging()) { lyxerr << "Cannot allocate grayscale " << num << endl; } AllocGrays(num / 2); return; } gs_pixels[i] = xcol.pixel; } gs_color = true; } static void InitFigures() { // if bitmaps and figures are not empty we will leak mem figures.clear(); bitmaps.clear(); // allocate color cube on pseudo-color display // first get visual gs_color = false; if (lyxrc.use_gui) { fl_add_canvas_handler(figinset_canvas, ClientMessage, GhostscriptMsg, current_view->owner()->getForm()); local_gc_copy = createGC(); Visual * vi = DefaultVisual(fl_get_display(), DefaultScreen(fl_get_display())); if (lyxerr.debugging()) { printf("Visual ID: %ld, class: %d, bprgb: %d, mapsz: %d\n", vi->visualid, vi->c_class, vi->bits_per_rgb, vi->map_entries); } color_visual = ( (vi->c_class == StaticColor) || (vi->c_class == PseudoColor) || (vi->c_class == TrueColor) || (vi->c_class == DirectColor) ); if ((vi->c_class & 1) == 0) return; // now allocate colors if (vi->c_class == GrayScale) { // allocate grayscale AllocGrays(vi->map_entries/2); } else { // allocate normal color int i = 5; while (i * i * i * 2 > vi->map_entries) --i; AllocColors(i); } gs_allcolors = vi->map_entries; } } static void DoneFigures() { // if bitmaps and figures are not empty we will leak mem bitmaps.clear(); figures.clear(); lyxerr.debug() << "Unregistering figures..." << endl; fl_remove_canvas_handler(figinset_canvas, ClientMessage, GhostscriptMsg); } static void freefigdata(figdata * tmpdata) { tmpdata->ref--; if (tmpdata->ref) return; if (tmpdata->gspid > 0) { int pid = tmpdata->gspid; // kill ghostscript and unlink it's files tmpdata->gspid = -1; kill_gs(pid, SIGKILL); } if (tmpdata->bitmap) XFreePixmap(fl_get_display(), tmpdata->bitmap); bitmaps.erase(find(bitmaps.begin(), bitmaps.end(), tmpdata)); delete tmpdata; } static void runqueue() { // This _have_ to be set before the fork! unsigned long background_pixel = lyxColorHandler->colorPixel(LColor::background); // run queued requests for ghostscript, if any if (!gsrunning && gs_color && !gs_xcolor) { // here alloc all colors, so that gs will use only // those we allocated for it // ***** gs_xcolor = true; } while (gsrunning < MAXGS) { //char tbuf[384]; //, tbuf2[80]; Atom * prop; int nprop, i; if (gsqueue.empty()) { if (!gsrunning && gs_xcolor) { // de-allocate rest of colors // ***** gs_xcolor = false; } return; } queue_element * p = &gsqueue.front(); if (!p->data) { gsqueue.pop(); continue; } int pid = ::fork(); if (pid == -1) { if (lyxerr.debugging()) { lyxerr << "GS start error! Cannot fork." << endl; } p->data->broken = true; p->data->reading = false; return; } if (pid == 0) { // child char ** env; int ne = 0; Display * tempdisp = XOpenDisplay(XDisplayName(0)); // create translation file ofstream ofs; ofs.open(make_tmp(getpid()).c_str()); ofs << "gsave clippath pathbbox grestore\n" << "4 dict begin\n" << "/ury exch def /urx exch def /lly exch def " "/llx exch def\n" << p->data->wid / 2.0 << " " << p->data->hgh / 2.0 << " translate\n" << p->data->angle << " rotate\n" << -(p->data->raw_wid / 2.0) << " " << -(p->data->raw_hgh / 2.0) << " translate\n" << p->rx / 72.0 << " " << p->ry / 72.0 << " scale\n" << -p->ofsx << " " << -p->ofsy << " translate\n" << "end" << endl; ofs.close(); // Don't remove this. // gs process - set ghostview environment first ostringstream t2; t2 << "GHOSTVIEW=" << fl_get_canvas_id(figinset_canvas) << ' ' << p->data->bitmap; // now set up ghostview property on a window // #warning BUG seems that the only bug here // might be the hardcoded dpi.. Bummer! ostringstream t1; t1 << "0 0 0 0 " << p->data->wid << ' ' << p->data->hgh << " 72 72 0 0 0 0"; if (lyxerr.debugging()) { lyxerr << "Will set GHOSTVIEW property to [" << t1.str() << "]" << endl; } // wait until property is deleted if executing multiple // ghostscripts XGrabServer(tempdisp); for (;;) { // grab server to prevent other child // interfering with setting GHOSTVIEW property // The grabbing goes on for too long, is it // really needed? (Lgb) // I moved most of the grabs... (Lgb) if (lyxerr.debugging()) { lyxerr << "Grabbing the server" << endl; } prop = XListProperties(tempdisp, fl_get_canvas_id( figinset_canvas), &nprop); if (!prop) break; bool err = true; for (i = 0; i < nprop; ++i) { char * p = XGetAtomName(tempdisp, prop[i]); if (strcmp(p, "GHOSTVIEW") == 0) { err = false; // We free it when we leave so we don't leak. XFree(p); break; } XFree(p); } XFree(reinterpret_cast(prop)); // jc: if (err) break; // ok, property found, we must wait until // ghostscript deletes it if (lyxerr.debugging()) { lyxerr << "Releasing the server\n[" << getpid() << "] GHOSTVIEW property" " found. Waiting." << endl; } XUngrabServer(tempdisp); XFlush(tempdisp); ::sleep(1); XGrabServer(tempdisp); } XChangeProperty(tempdisp, fl_get_canvas_id(figinset_canvas), XInternAtom(tempdisp, "GHOSTVIEW", false), XInternAtom(tempdisp, "STRING", false), 8, PropModeAppend, reinterpret_cast(const_cast(t1.str().c_str())), int(t1.str().size())); XUngrabServer(tempdisp); XFlush(tempdisp); ostringstream t3; switch (p->data->flags & 3) { case 0: t3 << 'H'; break; // Hidden case 1: t3 << 'M'; break; // Mono case 2: t3 << 'G'; break; // Gray case 3: if (color_visual) t3 << 'C'; // Color else t3 << 'G'; // Gray break; } t3 << ' ' << BlackPixelOfScreen(DefaultScreenOfDisplay(tempdisp)) << ' ' << background_pixel; XGrabServer(tempdisp); XChangeProperty(tempdisp, fl_get_canvas_id(figinset_canvas), XInternAtom(tempdisp, "GHOSTVIEW_COLORS", false), XInternAtom(tempdisp, "STRING", false), 8, PropModeReplace, reinterpret_cast(const_cast(t3.str().c_str())), int(t3.str().size())); XUngrabServer(tempdisp); XFlush(tempdisp); if (lyxerr.debugging()) { lyxerr << "Releasing the server" << endl; } XCloseDisplay(tempdisp); // set up environment while (environ[ne]) ++ne; typedef char * char_p; env = new char_p[ne + 2]; string tmp = t2.str().c_str(); env[0] = new char[tmp.size() + 1]; std::copy(tmp.begin(), tmp.end(), env[0]); env[0][tmp.size()] = '\0'; ::memcpy(&env[1], environ, sizeof(char*) * (ne + 1)); environ = env; // now make gs command // close(0); // close(1); do NOT close. If GS writes out // errors it would hang. (Matthias 290596) string rbuf = "-r" + tostr(p->rx) + "x" + tostr(p->ry); string gbuf = "-g" + tostr(p->data->wid) + "x" + tostr(p->data->hgh); // now chdir into dir with .eps file, to be on the safe // side lyx::chdir(OnlyPath(p->data->fname)); // make temp file name string tmpf = make_tmp(getpid()); if (lyxerr.debugging()) { lyxerr << "starting gs " << tmpf << " " << p->data->fname << ", pid: " << getpid() << endl; } int err = ::execlp(lyxrc.ps_command.c_str(), lyxrc.ps_command.c_str(), "-sDEVICE=x11", "-dNOPAUSE", "-dQUIET", "-dSAFER", rbuf.c_str(), gbuf.c_str(), tmpf.c_str(), p->data->fname.c_str(), "showpage.ps", "quit.ps", "-", 0); // if we are still there, an error occurred. lyxerr << "Error executing ghostscript. " << "Code: " << err << endl; lyxerr.debug() << "Cmd: " << lyxrc.ps_command << " -sDEVICE=x11 " << tmpf << ' ' << p->data->fname << endl; _exit(0); // no gs? } // normal process (parent) if (lyxerr.debugging()) { lyxerr << "GS [" << pid << "] started" << endl; } p->data->gspid = pid; ++gsrunning; gsqueue.pop(); } } static void addwait(int psx, int psy, int pswid, int pshgh, figdata * data) { // recompute the stuff and put in the queue queue_element p; p.ofsx = psx; p.ofsy = psy; p.rx = (float(data->raw_wid) * 72.0) / pswid; p.ry = (float(data->raw_hgh) * 72.0) / pshgh; p.data = data; gsqueue.push(p); // if possible, run the queue runqueue(); } static figdata * getfigdata(int wid, int hgh, string const & fname, int psx, int psy, int pswid, int pshgh, int raw_wid, int raw_hgh, float angle, char flags) { /* first search for an exact match with fname and width/height */ if (fname.empty() || !IsFileReadable(fname)) return 0; for (bitmaps_type::iterator it = bitmaps.begin(); it != bitmaps.end(); ++it) { if ((*it)->wid == wid && (*it)->hgh == hgh && (*it)->flags == flags && (*it)->fname == fname && (*it)->angle == angle) { (*it)->ref++; return (*it); } } figdata * p = new figdata; p->wid = wid; p->hgh = hgh; p->raw_wid = raw_wid; p->raw_hgh = raw_hgh; p->angle = angle; p->fname = fname; p->flags = flags; bitmaps.push_back(p); XWindowAttributes wa; XGetWindowAttributes(fl_get_display(), fl_get_canvas_id( figinset_canvas), &wa); if (lyxerr.debugging()) { lyxerr << "Create pixmap disp:" << fl_get_display() << " scr:" << DefaultScreen(fl_get_display()) << " w:" << wid << " h:" << hgh << " depth:" << wa.depth << endl; } p->ref = 1; p->reading = false; p->broken = false; p->gspid = -1; if (flags) { p->bitmap = XCreatePixmap(fl_get_display(), fl_get_canvas_id( figinset_canvas), wid, hgh, wa.depth); p->gsdone = false; // initialize reading of .eps file with correct sizes and stuff addwait(psx, psy, pswid, pshgh, p); p->reading = true; } else { p->bitmap = None; p->gsdone = true; } return p; } static void getbitmap(figdata * p) { p->gspid = -1; } static void makeupdatelist(figdata * p) { for(figures_type::iterator it = figures.begin(); it != figures.end(); ++it) if ((*it)->data == p) { if (lyxerr.debugging()) { lyxerr << "Updating inset " << (*it)->inset << endl; } // add inset figures[i]->inset into to_update list current_view->pushIntoUpdateList((*it)->inset); } } // this func is only "called" in spellchecker.C void sigchldchecker(pid_t pid, int * status) { lyxerr.debug() << "Got pid = " << pid << endl; bool pid_handled = false; for (bitmaps_type::iterator it = bitmaps.begin(); it != bitmaps.end(); ++it) { if ((*it)->reading && pid == (*it)->gspid) { lyxerr.debug() << "Found pid in bitmaps" << endl; // now read the file and remove it from disk figdata * p = (*it); p->reading = false; if ((*it)->gsdone) *status = 0; if (*status == 0) { lyxerr.debug() << "GS [" << pid << "] exit OK." << endl; } else { lyxerr << "GS [" << pid << "] error " << *status << " E:" << WIFEXITED(*status) << " " << WEXITSTATUS(*status) << " S:" << WIFSIGNALED(*status) << " " << WTERMSIG(*status) << endl; } if (*status == 0) { bitmap_waiting = true; p->broken = false; } else { // remove temporary files lyx::unlink(make_tmp(p->gspid)); p->gspid = -1; p->broken = true; } makeupdatelist((*it)); --gsrunning; runqueue(); pid_handled = true; } } if (!pid_handled) { lyxerr.debug() << "Checking pid in pidwait" << endl; list::iterator it = find(pidwaitlist.begin(), pidwaitlist.end(), pid); if (it != pidwaitlist.end()) { lyxerr.debug() << "Found pid in pidwait\n" << "Caught child pid of recompute " "routine" << pid << endl; pidwaitlist.erase(it); } } if (pid == -1) { lyxerr.debug() << "waitpid error" << endl; switch (errno) { case ECHILD: lyxerr << "The process or process group specified by " "pid does not exist or is not a child of " "the calling process or can never be in the " "states specified by options." << endl; break; case EINTR: lyxerr << "waitpid() was interrupted due to the " "receipt of a signal sent by the calling " "process." << endl; break; case EINVAL: lyxerr << "An invalid value was specified for " "options." << endl; break; default: lyxerr << "Unknown error from waitpid" << endl; break; } } else if (pid == 0) { lyxerr << "waitpid nohang" << endl;; } else { lyxerr.debug() << "normal exit from childhandler" << endl; } } static void getbitmaps() { bitmap_waiting = false; for (bitmaps_type::iterator it = bitmaps.begin(); it != bitmaps.end(); ++it) if ((*it)->gspid > 0 && !(*it)->reading) getbitmap((*it)); } static void RegisterFigure(InsetFig * fi) { if (figures.empty()) InitFigures(); fi->form = 0; Figref * tmpfig = new Figref; tmpfig->data = 0; tmpfig->inset = fi; figures.push_back(tmpfig); fi->figure = tmpfig; if (lyxerr.debugging()) { lyxerr << "Register Figure: buffer:[" << current_view->buffer() << "]" << endl; } } static void UnregisterFigure(InsetFig * fi) { if (!lyxrc.use_gui) return; Figref * tmpfig = fi->figure; if (tmpfig->data) freefigdata(tmpfig->data); if (tmpfig->inset->form) { if (tmpfig->inset->form->Figure->visible) { fl_set_focus_object(tmpfig->inset->form->Figure, tmpfig->inset->form->OkBtn); fl_hide_form(tmpfig->inset->form->Figure); } #if FL_REVISION == 89 // CHECK Reactivate this free_form calls #else fl_free_form(tmpfig->inset->form->Figure); free(tmpfig->inset->form); // Why free? tmpfig->inset->form = 0; #endif } figures.erase(find(figures.begin(), figures.end(), tmpfig)); delete tmpfig; if (figures.empty()) DoneFigures(); } InsetFig::InsetFig(int tmpx, int tmpy, Buffer const & o) : owner(&o) { wid = tmpx; hgh = tmpy; wtype = DEF; htype = DEF; twtype = DEF; thtype = DEF; pflags = flags = 9; psubfigure = subfigure = false; xwid = xhgh = angle = 0; pswid = pshgh = 0; raw_wid = raw_hgh = 0; changedfname = false; RegisterFigure(this); } InsetFig::~InsetFig() { if (lyxerr.debugging()) { lyxerr << "Figure destructor called" << endl; } UnregisterFigure(this); } int InsetFig::ascent(BufferView *, LyXFont const &) const { return hgh + 3; } int InsetFig::descent(BufferView *, LyXFont const &) const { return 1; } int InsetFig::width(BufferView *, LyXFont const &) const { return wid + 2; } void InsetFig::draw(BufferView * bv, LyXFont const & f, int baseline, float & x, bool) const { LyXFont font(f); Painter & pain = bv->painter(); if (bitmap_waiting) getbitmaps(); // I wish that I didn't have to use this // but the figinset code is so complicated so // I don't want to fiddle with it now. if (figure && figure->data && figure->data->bitmap && !figure->data->reading && !figure->data->broken) { // draw the bitmap pain.pixmap(int(x + 1), baseline - hgh, wid, hgh, figure->data->bitmap); if (flags & 4) pain.rectangle(int(x), baseline - hgh - 1, wid + 1, hgh + 1); } else { char const * msg = 0; string lfname = fname; if (!fname.empty() && GetExtension(fname).empty()) lfname += ".eps"; // draw frame pain.rectangle(int(x), baseline - hgh - 1, wid + 1, hgh + 1); if (figure && figure->data) { if (figure->data->broken) msg = _("[render error]"); else if (figure->data->reading) msg = _("[rendering ... ]"); } else if (fname.empty()) msg = _("[no file]"); else if (!IsFileReadable(lfname)) msg = _("[bad file name]"); else if ((flags & 3) == 0) msg = _("[not displayed]"); else if (lyxrc.ps_command.empty()) msg = _("[no ghostscript]"); if (!msg) msg = _("[unknown error]"); font.setFamily(LyXFont::SANS_FAMILY); font.setSize(LyXFont::SIZE_FOOTNOTE); string justname = OnlyFilename (fname); pain.text(int(x + 8), baseline - lyxfont::maxAscent(font) - 4, justname, font); font.setSize(LyXFont::SIZE_TINY); pain.text(int(x + 8), baseline - 4, msg, strlen(msg), font); } x += width(bv, font); // ? } void InsetFig::Write(Buffer const *, ostream & os) const { Regenerate(); os << "Figure size " << wid << " " << hgh << "\n"; if (!fname.empty()) { string buf1 = OnlyPath(owner->fileName()); string fname2 = MakeRelPath(fname, buf1); os << "file " << fname2 << "\n"; } if (!subcaption.empty()) os << "subcaption " << subcaption << "\n"; if (wtype) os << "width " << static_cast(wtype) << " " << xwid << "\n"; if (htype) os << "height " << static_cast(htype) << " " << xhgh << "\n"; if (angle != 0) os << "angle " << angle << "\n"; os << "flags " << flags << "\n"; if (subfigure) os << "subfigure\n"; } void InsetFig::Read(Buffer const *, LyXLex & lex) { string buf; bool finished = false; while (lex.IsOK() && !finished) { lex.next(); string const token = lex.GetString(); lyxerr.debug() << "Token: " << token << endl; if (token.empty()) continue; else if (token == "\\end_inset") { finished = true; } else if (token == "file") { if (lex.next()) { buf = lex.GetString(); string buf1 = OnlyPath(owner->fileName()); fname = MakeAbsPath(buf, buf1); changedfname = true; } } else if (token == "extra") { if (lex.next()); // kept for backwards compability. Delete in 0.13.x } else if (token == "subcaption") { if (lex.EatLine()) subcaption = lex.GetString(); } else if (token == "label") { if (lex.next()); // kept for backwards compability. Delete in 0.13.x } else if (token == "angle") { if (lex.next()) angle = lex.GetFloat(); } else if (token == "size") { if (lex.next()) wid = lex.GetInteger(); if (lex.next()) hgh = lex.GetInteger(); } else if (token == "flags") { if (lex.next()) flags = pflags = lex.GetInteger(); } else if (token == "subfigure") { subfigure = psubfigure = true; } else if (token == "width") { int typ = 0; if (lex.next()) typ = lex.GetInteger(); if (lex.next()) xwid = lex.GetFloat(); switch (typ) { case DEF: wtype = DEF; break; case CM: wtype = CM; break; case IN: wtype = IN; break; case PER_PAGE: wtype = PER_PAGE; break; case PER_COL: wtype = PER_COL; break; default: lyxerr.debug() << "Unknown type!" << endl; break; } twtype = wtype; } else if (token == "height") { int typ = 0; if (lex.next()) typ = lex.GetInteger(); if (lex.next()) xhgh = lex.GetFloat(); switch (typ) { case DEF: htype = DEF; break; case CM: htype = CM; break; case IN: htype = IN; break; case PER_PAGE: htype = PER_PAGE; break; default: lyxerr.debug() << "Unknown type!" << endl; break; } thtype = htype; } } Regenerate(); Recompute(); } int InsetFig::Latex(Buffer const *, ostream & os, bool /* fragile*/, bool /* fs*/) const { Regenerate(); if (!cmd.empty()) os << cmd << " "; return 0; } int InsetFig::Ascii(Buffer const *, ostream &, int) const { return 0; } int InsetFig::Linuxdoc(Buffer const *, ostream &) const { return 0; } int InsetFig::DocBook(Buffer const *, ostream & os) const { string buf1 = OnlyPath(owner->fileName()); string figurename = MakeRelPath(fname, buf1); if(suffixIs(figurename, ".eps")) figurename.erase(figurename.length() - 4); os << "@"; return 0; } void InsetFig::Validate(LaTeXFeatures & features) const { features.graphics = true; if (subfigure) features.subfigure = true; } Inset::EDITABLE InsetFig::Editable() const { return IS_EDITABLE; } bool InsetFig::Deletable() const { return false; } string const InsetFig::EditMessage() const { return _("Opened figure"); } void InsetFig::Edit(BufferView * bv, int, int, unsigned int) { lyxerr.debug() << "Editing InsetFig." << endl; Regenerate(); // We should have RO-versions of the form instead. // The actual prevention of altering a readonly doc // is done in CallbackFig() if(bv->buffer()->isReadonly()) WarnReadonly(bv->buffer()->fileName()); if (!form) { form = create_form_Figure(); fl_set_form_atclose(form->Figure, CancelCloseBoxCB, 0); fl_set_object_return(form->Angle, FL_RETURN_ALWAYS); fl_set_object_return(form->Width, FL_RETURN_ALWAYS); fl_set_object_return(form->Height, FL_RETURN_ALWAYS); } RestoreForm(); if (form->Figure->visible) { fl_raise_form(form->Figure); } else { fl_show_form(form->Figure, FL_PLACE_MOUSE | FL_PLACE_SIZE, FL_FULLBORDER, _("Figure")); } } Inset * InsetFig::Clone(Buffer const & buffer) const { InsetFig * tmp = new InsetFig(100, 100, buffer); if (lyxerr.debugging()) { lyxerr << "Clone Figure: buffer:[" << &buffer << "], cbuffer:[xx]" << endl; } tmp->wid = wid; tmp->hgh = hgh; tmp->raw_wid = raw_wid; tmp->raw_hgh = raw_hgh; tmp->angle = angle; tmp->xwid = xwid; tmp->xhgh = xhgh; tmp->flags = flags; tmp->pflags = pflags; tmp->subfigure = subfigure; tmp->psubfigure = psubfigure; tmp->wtype = wtype; tmp->htype = htype; tmp->psx = psx; tmp->psy = psy; tmp->pswid = pswid; tmp->pshgh = pshgh; tmp->fname = fname; if (!fname.empty() && IsFileReadable(fname) && (flags & 3) && !lyxrc.ps_command.empty() && lyxrc.use_gui) { // do not display if there is // "do not display" chosen (Matthias 260696) tmp->figure->data = getfigdata(wid, hgh, fname, psx, psy, pswid, pshgh, raw_wid, raw_hgh, angle, flags & (3|8)); } else tmp->figure->data = 0; tmp->subcaption = subcaption; tmp->changedfname = false; tmp->owner = owner; tmp->Regenerate(); return tmp; } Inset::Code InsetFig::LyxCode() const { return Inset::GRAPHICS_CODE; } static string stringify(InsetFig::HWTYPE hw, float f, string suffix) { string res; switch (hw) { case InsetFig::DEF: break; case InsetFig::CM:// \resizebox*{h-length}{v-length}{text} res = tostr(f) + "cm"; break; case InsetFig::IN: res = tostr(f) + "in"; break; case InsetFig::PER_PAGE: res = tostr(f/100) + "\\text" + suffix; break; case InsetFig::PER_COL: // Doesn't occur for htype... res = tostr(f/100) + "\\column" + suffix; break; } return res; } void InsetFig::Regenerate() const { string cmdbuf; string resizeW, resizeH; string rotate, recmd; if (fname.empty()) { cmd = "\\fbox{\\rule[-0.5in]{0pt}{1in}"; cmd += _("empty figure path"); cmd += '}'; return; } string buf1 = OnlyPath(owner->fileName()); string fname2 = MakeRelPath(fname, buf1); string gcmd = "\\includegraphics{" + fname2 + '}'; resizeW = stringify(wtype, xwid, "width"); resizeH = stringify(htype, xhgh, "height"); if (!resizeW.empty() || !resizeH.empty()) { recmd = "\\resizebox*{"; if (!resizeW.empty()) recmd += resizeW; else recmd += '!'; recmd += "}{"; if (!resizeH.empty()) recmd += resizeH; else recmd += '!'; recmd += "}{"; } if (angle != 0) { // \rotatebox{angle}{text} rotate = "\\rotatebox{" + tostr(angle) + "}{"; } cmdbuf = recmd; cmdbuf += rotate; cmdbuf += gcmd; if (!rotate.empty()) cmdbuf += '}'; if (!recmd.empty()) cmdbuf += '}'; if (subfigure) { if (!subcaption.empty()) cmdbuf = "\\subfigure[" + subcaption + "]{" + cmdbuf + "}"; else cmdbuf = "\\subfigure{" + cmdbuf + "}"; } cmd = cmdbuf; } void InsetFig::TempRegenerate() { string cmdbuf; string resizeW, resizeH; string rotate, recmd; char const * tfname = fl_get_input(form->EpsFile); string tsubcap = fl_get_input(form->Subcaption); float tangle = atof(fl_get_input(form->Angle)); float txwid = atof(fl_get_input(form->Width)); float txhgh = atof(fl_get_input(form->Height)); if (!tfname || !*tfname) { cmd = "\\fbox{\\rule[-0.5in]{0pt}{1in}"; cmd += _("empty figure path"); cmd += '}'; return; } string buf1 = OnlyPath(owner->fileName()); string fname2 = MakeRelPath(tfname, buf1); // \includegraphics*[][]{file} string gcmd = "\\includegraphics{" + fname2 + '}'; resizeW = stringify(twtype, txwid, "width"); resizeH = stringify(thtype, txhgh, "height"); // \resizebox*{h-length}{v-length}{text} if (!resizeW.empty() || !resizeH.empty()) { recmd = "\\resizebox*{"; if (!resizeW.empty()) recmd += resizeW; else recmd += '!'; recmd += "}{"; if (!resizeH.empty()) recmd += resizeH; else recmd += '!'; recmd += "}{"; } if (tangle != 0) { // \rotatebox{angle}{text} rotate = "\\rotatebox{" + tostr(tangle) + "}{"; } cmdbuf = recmd + rotate + gcmd; if (!rotate.empty()) cmdbuf += '}'; if (!recmd.empty()) cmdbuf += '}'; if (psubfigure && !tsubcap.empty()) { cmdbuf = string("\\subfigure{") + tsubcap + string("}{") + cmdbuf + "}"; } } void InsetFig::Recompute() { if (!lyxrc.use_gui) return; bool changed = changedfname; int newx, newy, nraw_x, nraw_y; if (changed) GetPSSizes(); float sin_a = sin (angle / DEG2PI); /* rotation; H. Zeller 021296 */ float cos_a = cos (angle / DEG2PI); int frame_wid = int(ceil(fabs(cos_a * pswid) + fabs(sin_a * pshgh))); int frame_hgh= int(ceil(fabs(cos_a * pshgh) + fabs(sin_a * pswid))); string lfname = fname; if (GetExtension(fname).empty()) lfname += ".eps"; /* now recompute wid and hgh, and if that is changed, set changed */ /* this depends on chosen size of the picture and its bbox */ // This will be redone in 0.13 ... (hen) if (!lfname.empty() && IsFileReadable(lfname)) { // say, total width is 595 pts, as A4 in TeX, thats in 1/72" */ newx = frame_wid; newy = frame_hgh; switch (wtype) { case DEF: break; case CM: /* cm */ newx = int(28.346 * xwid); break; case IN: /* in */ newx = int(72 * xwid); break; case PER_PAGE: /* % of page */ newx = int(5.95 * xwid); break; case PER_COL: /* % of col */ newx = int(2.975 * xwid); break; } if (wtype && frame_wid) newy = newx*frame_hgh/frame_wid; switch (htype) { case DEF: //lyxerr << "This should not happen!" << endl; break; case CM: /* cm */ newy = int(28.346 * xhgh); break; case IN: /* in */ newy = int(72 * xhgh); break; case PER_PAGE: /* % of page */ newy = int(8.42 * xhgh); break; case PER_COL: // Doesn't occur; case exists to suppress // compiler warnings. break; } if (htype && !wtype && frame_hgh) newx = newy*frame_wid/frame_hgh; } else { newx = wid; newy = hgh; } if (frame_wid == 0) nraw_x = 5; else nraw_x = int((1.0 * pswid * newx)/frame_wid); if (frame_hgh == 0) nraw_y = 5; else nraw_y = int((1.0 * pshgh * newy)/frame_hgh); // cannot be zero, actually, set it to some minimum, so its clickable if (newx < 5) newx = 5; if (newy < 5) newy = 5; if (newx != wid || newy != hgh || nraw_x != raw_wid || nraw_y != raw_hgh || flags != pflags || subfigure != psubfigure) changed = true; raw_wid = nraw_x; raw_hgh = nraw_y; wid = newx; hgh = newy; flags = pflags; subfigure = psubfigure; if (changed) { figdata * pf = figure->data; // get new data if (!lfname.empty() && IsFileReadable(lfname) && (flags & 3) && !lyxrc.ps_command.empty()) { // do not display if there is "do not display" // chosen (Matthias 260696) figure->data = getfigdata(wid, hgh, lfname, psx, psy, pswid, pshgh, raw_wid, raw_hgh, angle, flags & (3|8)); } else figure->data = 0; // free the old data if (pf) freefigdata(pf); } changedfname = false; } void InsetFig::GetPSSizes() { /* get %%BoundingBox: from postscript file */ /* defaults to associated size * ..just in case the PS-file is not readable (Henner, 24-Aug-97) */ psx = 0; psy = 0; pswid = wid; pshgh = hgh; if (fname.empty()) return; string p; ifstream ifs(fname.c_str()); if (!ifs) return; // file not found !!!! /* defaults to A4 page */ psx = 0; psy = 0; pswid = 595; pshgh = 842; char lastchar = 0; ifs.get(lastchar); for (;;) { char c = 0; ifs.get(c); if (ifs.eof()) { lyxerr.debug() << "End of (E)PS file reached and" " no BoundingBox!" << endl; break; } if (c == '%' && lastchar == '%') { ifs >> p; if (p.empty()) break; lyxerr.debug() << "Token: `" << p << "'" << endl; if (p == "BoundingBox:") { float fpsx, fpsy, fpswid, fpshgh; if (ifs >> fpsx >> fpsy >> fpswid >> fpshgh) { psx = int(fpsx); psy = int(fpsy); pswid = int(fpswid); pshgh = int(fpshgh); } if (lyxerr.debugging()) { lyxerr << "%%%%BoundingBox:" << psx << ' ' << psy << ' ' << pswid << ' ' << pshgh << endl; } break; } c = 0; } lastchar = c; } pswid -= psx; pshgh -= psy; } void InsetFig::CallbackFig(long arg) { bool regen = false; char const * p; if (lyxerr.debugging()) { lyxerr << "Figure callback, arg " << arg << endl; } switch (arg) { case 10: case 11: case 12: /* width type */ case 13: case 14: switch (arg - 10) { case DEF: twtype = DEF; // put disable here fl_deactivate_object(form->Width); break; case CM: twtype = CM; // put enable here fl_activate_object(form->Width); break; case IN: twtype = IN; // put enable here fl_activate_object(form->Width); break; case PER_PAGE: twtype = PER_PAGE; // put enable here fl_activate_object(form->Width); break; case PER_COL: twtype = PER_COL; // put enable here fl_activate_object(form->Width); break; default: lyxerr.debug() << "Unknown type!" << endl; break; } regen = true; break; case 20: case 21: case 22: /* height type */ case 23: switch (arg - 20) { case DEF: thtype = DEF; // put disable here fl_deactivate_object(form->Height); break; case CM: thtype = CM; // put enable here fl_activate_object(form->Height); break; case IN: thtype = IN; // put enable here fl_activate_object(form->Height); break; case PER_PAGE: thtype = PER_PAGE; // put enable here fl_activate_object(form->Height); break; default: lyxerr.debug() << "Unknown type!" << endl; break; } regen = true; break; case 3: pflags = pflags & ~3; /* wysiwyg0 */ break; case 33: pflags = (pflags & ~3) | 1; /* wysiwyg1 */ break; case 43: pflags = (pflags & ~3) | 2; /* wysiwyg2 */ break; case 63: pflags = (pflags & ~3) | 3; /* wysiwyg3 */ break; case 53: pflags ^= 4; /* frame */ break; case 54: pflags ^= 8; /* do translations */ break; case 70: psubfigure = !psubfigure; /* This is a subfigure */ break; case 2: regen = true; /* regenerate command */ break; case 0: /* browse file */ BrowseFile(); regen = true; break; case 1: /* preview */ p = fl_get_input(form->EpsFile); Preview(p); break; case 7: /* apply */ case 8: /* ok (apply and close) */ if(!current_view->buffer()->isReadonly()) { wtype = twtype; htype = thtype; xwid = atof(fl_get_input(form->Width)); xhgh = atof(fl_get_input(form->Height)); angle = atof(fl_get_input(form->Angle)); p = fl_get_input(form->EpsFile); if (p && *p) { string buf1 = OnlyPath(owner->fileName()); fname = MakeAbsPath(p, buf1); changedfname = true; } else { if (!fname.empty()) { changedfname = true; fname.erase(); } } subcaption = fl_get_input(form->Subcaption); Regenerate(); Recompute(); /* now update inset */ if (lyxerr.debugging()) { lyxerr << "Update: [" << wid << 'x' << hgh << ']' << endl; } current_view->updateInset(this, true); if (arg == 8) { fl_set_focus_object(form->Figure, form->OkBtn); fl_hide_form(form->Figure); #if FL_REVISION == 89 // CHECK Reactivate this free_form calls #else fl_free_form(form->Figure); free(form); // Why free? form = 0; #endif } break; } //if not readonly // The user has already been informed about RO in ::Edit if(arg == 7) // if 'Apply' break; // fall through case 9: /* cancel = restore and close */ fl_set_focus_object(form->Figure, form->OkBtn); fl_hide_form(form->Figure); #if FL_REVISION == 89 // CHECK Reactivate this free_form calls // Jug, is this still a problem? #else fl_free_form(form->Figure); free(form); // Why free? form = 0; #endif break; } if (regen) TempRegenerate(); } inline void DisableFigurePanel(FD_Figure * const form) { fl_deactivate_object(form->EpsFile); fl_deactivate_object(form->Browse); fl_deactivate_object(form->Width); fl_deactivate_object(form->Height); fl_deactivate_object(form->Frame); fl_deactivate_object(form->Translations); fl_deactivate_object(form->Angle); fl_deactivate_object(form->HeightGrp); fl_deactivate_object(form->page2); fl_deactivate_object(form->Default2); fl_deactivate_object(form->cm2); fl_deactivate_object(form->in2); fl_deactivate_object(form->HeightLabel); fl_deactivate_object(form->WidthLabel); fl_deactivate_object(form->DisplayGrp); fl_deactivate_object(form->Wysiwyg3); fl_deactivate_object(form->Wysiwyg0); fl_deactivate_object(form->Wysiwyg2); fl_deactivate_object(form->Wysiwyg1); fl_deactivate_object(form->WidthGrp); fl_deactivate_object(form->Default1); fl_deactivate_object(form->cm1); fl_deactivate_object(form->in1); fl_deactivate_object(form->page1); fl_deactivate_object(form->column1); fl_deactivate_object(form->Subcaption); fl_deactivate_object(form->Subfigure); fl_deactivate_object (form->OkBtn); fl_deactivate_object (form->ApplyBtn); fl_set_object_lcol (form->OkBtn, FL_INACTIVE); fl_set_object_lcol (form->ApplyBtn, FL_INACTIVE); } inline void EnableFigurePanel(FD_Figure * const form) { fl_activate_object(form->EpsFile); fl_activate_object(form->Browse); fl_activate_object(form->Width); fl_activate_object(form->Height); fl_activate_object(form->Frame); fl_activate_object(form->Translations); fl_activate_object(form->Angle); fl_activate_object(form->HeightGrp); fl_activate_object(form->page2); fl_activate_object(form->Default2); fl_activate_object(form->cm2); fl_activate_object(form->in2); fl_activate_object(form->HeightLabel); fl_activate_object(form->WidthLabel); fl_activate_object(form->DisplayGrp); fl_activate_object(form->Wysiwyg3); fl_activate_object(form->Wysiwyg0); fl_activate_object(form->Wysiwyg2); fl_activate_object(form->Wysiwyg1); fl_activate_object(form->WidthGrp); fl_activate_object(form->Default1); fl_activate_object(form->cm1); fl_activate_object(form->in1); fl_activate_object(form->page1); fl_activate_object(form->column1); fl_activate_object(form->Subcaption); fl_activate_object(form->Subfigure); fl_activate_object (form->OkBtn); fl_activate_object (form->ApplyBtn); fl_set_object_lcol (form->OkBtn, FL_BLACK); fl_set_object_lcol (form->ApplyBtn, FL_BLACK); } void InsetFig::RestoreForm() { EnableFigurePanel(form); twtype = wtype; fl_set_button(form->Default1, (wtype == 0)); fl_set_button(form->cm1, (wtype == 1)); fl_set_button(form->in1, (wtype == 2)); fl_set_button(form->page1, (wtype == 3)); fl_set_button(form->column1, (wtype == 4)); if (wtype == 0) { fl_deactivate_object(form->Width); } else { fl_activate_object(form->Width); } // enable and disable should be put here. thtype = htype; fl_set_button(form->Default2, (htype == 0)); fl_set_button(form->cm2, (htype == 1)); fl_set_button(form->in2, (htype == 2)); fl_set_button(form->page2, (htype == 3)); // enable and disable should be put here. if (htype == 0) { fl_deactivate_object(form->Height); } else { fl_activate_object(form->Height); } int piflags = flags & 3; fl_set_button(form->Wysiwyg0, (piflags == 0)); fl_set_button(form->Wysiwyg1, (piflags == 1)); fl_set_button(form->Wysiwyg2, (piflags == 2)); fl_set_button(form->Wysiwyg3, (piflags == 3)); fl_set_button(form->Frame, ((flags & 4) != 0)); fl_set_button(form->Translations, ((flags & 8) != 0)); fl_set_button(form->Subfigure, (subfigure != 0)); pflags = flags; psubfigure = subfigure; fl_set_input(form->Width, tostr(xwid).c_str()); fl_set_input(form->Height, tostr(xhgh).c_str()); fl_set_input(form->Angle, tostr(angle).c_str()); if (!fname.empty()){ string buf1 = OnlyPath(owner->fileName()); string fname2 = MakeRelPath(fname, buf1); fl_set_input(form->EpsFile, fname2.c_str()); } else fl_set_input(form->EpsFile, ""); fl_set_input(form->Subcaption, subcaption.c_str()); if(current_view->buffer()->isReadonly()) DisableFigurePanel(form); TempRegenerate(); } void InsetFig::Preview(string const & p) { string tfname = p; if (GetExtension(tfname).empty()) tfname += ".eps"; string buf1 = OnlyPath(owner->fileName()); string buf2 = MakeAbsPath(tfname, buf1); if (!Formats::View(owner, buf2, "eps")) lyxerr << "Can't view " << buf2 << endl; } void InsetFig::BrowseFile() { static string current_figure_path; static int once = 0; LyXFileDlg fileDlg; if (lyxerr.debugging()) { lyxerr << "Filename: " << owner->fileName() << endl; } string p = fl_get_input(form->EpsFile); string buf = MakeAbsPath(owner->fileName()); string buf2 = OnlyPath(buf); if (!p.empty()) { buf = MakeAbsPath(p, buf2); buf = OnlyPath(buf); } else { buf = OnlyPath(owner->fileName()); } // Does user clipart directory exist? string bufclip = AddName (user_lyxdir, "clipart"); FileInfo fileInfo(bufclip); if (!(fileInfo.isOK() && fileInfo.isDir())) // No - bail out to system clipart directory bufclip = AddName (system_lyxdir, "clipart"); fileDlg.SetButton(0, _("Clipart"), bufclip); fileDlg.SetButton(1, _("Document"), buf); bool error = false; do { ProhibitInput(current_view); if (once) { p = fileDlg.Select(_("EPS Figure"), current_figure_path, "*ps", string()); } else { p = fileDlg.Select(_("EPS Figure"), buf, "*ps", string()); } AllowInput(current_view); if (p.empty()) return; buf = MakeRelPath(p, buf2); current_figure_path = OnlyPath(p); once = 1; if (contains(p, "#") || contains(p, "~") || contains(p, "$") || contains(p, "%") || contains(p, " ")) { WriteAlert(_("Filename can't contain any " "of these characters:"), // xgettext:no-c-format _("space, '#', '~', '$' or '%'.")); error = true; } } while (error); if (form) fl_set_input(form->EpsFile, buf.c_str()); } void GraphicsCB(FL_OBJECT * obj, long arg) { /* obj->form contains the form */ if (lyxerr.debugging()) { lyxerr << "GraphicsCB callback: " << arg << endl; } /* find inset we were reacting to */ for (figures_type::iterator it = figures.begin(); it != figures.end(); ++it) if ((*it)->inset->form && (*it)->inset->form->Figure == obj->form) { if (lyxerr.debugging()) { lyxerr << "Calling back figure " << (*it) << endl; } (*it)->inset->CallbackFig(arg); return; } } void HideFiguresPopups() { for (figures_type::iterator it = figures.begin(); it != figures.end(); ++it) if ((*it)->inset->form && (*it)->inset->form->Figure->visible) { if (lyxerr.debugging()) { lyxerr << "Hiding figure " << (*it) << endl; } // hide and free the form (*it)->inset->CallbackFig(9); } }