__init_fltk__.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2007-2012 Shai Ayal
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 /*
00024 
00025 To initialize:
00026 
00027   graphics_toolkit ("fltk");
00028   plot (randn (1e3, 1));
00029 
00030 */
00031 
00032 // PKG_ADD: register_graphics_toolkit ("fltk");
00033 
00034 #ifdef HAVE_CONFIG_H
00035 #include <config.h>
00036 #endif
00037 
00038 #include "defun-dld.h"
00039 #include "error.h"
00040 
00041 #if defined (HAVE_FLTK)
00042 
00043 #include <map>
00044 #include <set>
00045 #include <sstream>
00046 #include <iostream>
00047 
00048 #ifdef WIN32
00049 #define WIN32_LEAN_AND_MEAN
00050 #endif
00051 
00052 #include <FL/Fl.H>
00053 #include <FL/Fl_Box.H>
00054 #include <FL/Fl_Button.H>
00055 #include <FL/Fl_Choice.H>
00056 #include <FL/Fl_File_Chooser.H>
00057 #include <FL/Fl_Gl_Window.H>
00058 #include <FL/Fl_Menu_Bar.H>
00059 #include <FL/Fl_Menu_Button.H>
00060 #include <FL/Fl_Output.H>
00061 #include <FL/Fl_Window.H>
00062 #include <FL/fl_ask.H>
00063 #include <FL/fl_draw.H>
00064 #include <FL/gl.h>
00065 
00066 // FLTK headers may include X11/X.h which defines Complex, and that
00067 // conflicts with Octave's Complex typedef.  We don't need the X11
00068 // Complex definition in this file, so remove it before including Octave
00069 // headers which may require Octave's Complex typedef.
00070 #undef Complex
00071 
00072 #include "cmd-edit.h"
00073 #include "lo-ieee.h"
00074 
00075 #include "file-ops.h"
00076 #include "gl-render.h"
00077 #include "gl2ps-renderer.h"
00078 #include "graphics.h"
00079 #include "parse.h"
00080 #include "sysdep.h"
00081 #include "toplev.h"
00082 #include "variables.h"
00083 
00084 #define FLTK_GRAPHICS_TOOLKIT_NAME "fltk"
00085 
00086 // Give FLTK no more than 0.01 sec to do its stuff.
00087 static double fltk_maxtime = 1e-2;
00088 
00089 const char* help_text = "\
00090 Keyboard Shortcuts\n\
00091 a - autoscale\n\
00092 p - pan/zoom\n\
00093 r - rotate\n\
00094 g - toggle grid\n\
00095 \n\
00096 Mouse\n\
00097 left drag - pan\n\
00098 mouse wheel - zoom\n\
00099 right drag - rectangle zoom\n\
00100 left double click - autoscale\n\
00101 ";
00102 
00103 class OpenGL_fltk : public Fl_Gl_Window
00104 {
00105 public:
00106   OpenGL_fltk (int xx, int yy, int ww, int hh, double num)
00107     : Fl_Gl_Window (xx, yy, ww, hh, 0), number (num), renderer (),
00108       in_zoom (false), zoom_box (),  print_mode (false)
00109   {
00110     // Ask for double buffering and a depth buffer.
00111     mode (FL_DEPTH | FL_DOUBLE);
00112   }
00113 
00114   ~OpenGL_fltk (void) { }
00115 
00116   void zoom (bool z)
00117   {
00118     in_zoom = z;
00119     if (! in_zoom)
00120       hide_overlay ();
00121   }
00122 
00123   bool zoom (void) { return in_zoom; }
00124   void set_zoom_box (const Matrix& zb) { zoom_box = zb; }
00125 
00126   void print (const std::string& cmd, const std::string& term)
00127   {
00128     print_mode  = true;
00129     print_cmd = cmd;
00130     print_term  = term;
00131   }
00132 
00133   void resize (int xx, int yy, int ww, int hh)
00134   {
00135     Fl_Gl_Window::resize (xx, yy, ww, hh);
00136     setup_viewport (ww, hh);
00137     redraw ();
00138   }
00139 
00140   bool renumber (double new_number)
00141   {
00142     bool retval = false;
00143 
00144     if (number != new_number)
00145       {
00146         number = new_number;
00147         retval = true;
00148       }
00149 
00150     return retval;
00151   }
00152 
00153 private:
00154   double number;
00155   opengl_renderer renderer;
00156   bool in_zoom;
00157   // (x1,y1,x2,y2)
00158   Matrix zoom_box;
00159 
00160   bool print_mode;
00161   std::string print_cmd;
00162   std::string print_term;
00163 
00164   void setup_viewport (int ww, int hh)
00165   {
00166     glMatrixMode (GL_PROJECTION);
00167     glLoadIdentity ();
00168     glViewport (0, 0, ww, hh);
00169   }
00170 
00171   void draw (void)
00172   {
00173     if (! valid ())
00174       {
00175         valid (1);
00176         setup_viewport (w (), h ());
00177       }
00178 
00179     if (print_mode)
00180       {
00181         FILE *fp = octave_popen (print_cmd.c_str (), "w");
00182         glps_renderer rend (fileno (fp), print_term);
00183 
00184         rend.draw (gh_manager::get_object (number));
00185 
00186         octave_pclose (fp);
00187         print_mode = false;
00188       }
00189     else
00190       {
00191         renderer.draw (gh_manager::get_object (number));
00192 
00193         if (zoom ())
00194           overlay ();
00195       }
00196   }
00197 
00198   void zoom_box_vertex (void)
00199   {
00200     glVertex2d (zoom_box(0), h () - zoom_box(1));
00201     glVertex2d (zoom_box(0), h () - zoom_box(3));
00202     glVertex2d (zoom_box(2), h () - zoom_box(3));
00203     glVertex2d (zoom_box(2), h () - zoom_box(1));
00204     glVertex2d (zoom_box(0), h () - zoom_box(1));
00205   }
00206 
00207   void overlay (void)
00208   {
00209     glPushMatrix ();
00210 
00211     glMatrixMode (GL_MODELVIEW);
00212     glLoadIdentity ();
00213 
00214     glMatrixMode (GL_PROJECTION);
00215     glLoadIdentity ();
00216     gluOrtho2D (0.0, w (), 0.0, h ());
00217 
00218     glPushAttrib (GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT);
00219     glDisable (GL_DEPTH_TEST);
00220 
00221     glBegin (GL_POLYGON);
00222     glColor4f (0.45, 0.62, 0.81, 0.1);
00223     zoom_box_vertex ();
00224     glEnd ();
00225 
00226     glBegin (GL_LINE_STRIP);
00227     glLineWidth (1.5);
00228     glColor4f (0.45, 0.62, 0.81, 0.9);
00229     zoom_box_vertex ();
00230     glEnd ();
00231 
00232     glPopAttrib ();
00233     glPopMatrix ();
00234   }
00235 
00236   int handle (int event)
00237   {
00238     int retval = Fl_Gl_Window::handle (event);
00239 
00240     switch (event)
00241       {
00242       case FL_ENTER:
00243         window ()->cursor (FL_CURSOR_CROSS);
00244         return 1;
00245 
00246       case FL_LEAVE:
00247         window ()->cursor (FL_CURSOR_DEFAULT);
00248         return 1;
00249       }
00250 
00251     return retval;
00252   }
00253 };
00254 
00255 // Parameter controlling how fast we zoom when using the scrool wheel.
00256 static double wheel_zoom_speed = 0.05;
00257 // Parameter controlling the GUI mode.
00258 static enum { pan_zoom, rotate_zoom, none } gui_mode;
00259 
00260 void script_cb(Fl_Widget*, void* data)
00261   {
00262     static_cast<uimenu::properties*> (data)->execute_callback ();
00263   }
00264 
00265 
00266 class fltk_uimenu
00267 {
00268 public:
00269   fltk_uimenu (int xx, int yy, int ww, int hh)
00270     {
00271       menubar = new
00272         Fl_Menu_Bar(xx, yy, ww, hh);
00273     }
00274 
00275   int items_to_show (void)
00276     {
00277       //returns the number of visible menu items
00278       int len = menubar->size ();
00279       int n = 0;
00280       for (int t = 0; t < len; t++ )
00281         {
00282           const Fl_Menu_Item *m = static_cast<const Fl_Menu_Item*> (&(menubar->menu ()[t]));
00283           if ((m->label () != NULL) && m->visible ())
00284             n++;
00285         }
00286 
00287       return n;
00288     }
00289 
00290   void show (void)
00291     {
00292       menubar->show ();
00293     }
00294 
00295   void hide (void)
00296     {
00297       menubar->hide ();
00298     }
00299 
00300    bool is_visible (void)
00301     {
00302       return menubar->visible ();
00303     }
00304 
00305   int find_index_by_name (const std::string& findname)
00306     {
00307       // This function is derived from Greg Ercolano's function
00308       // int GetIndexByName(...), see:
00309       // http://seriss.com/people/erco/fltk/#Menu_ChangeLabel
00310       // He agreed via PM that it can be included in octave using GPLv3
00311       // Kai Habel (14.10.2010)
00312 
00313       std::string menupath;
00314       for (int t = 0; t < menubar->size (); t++ )
00315         {
00316           Fl_Menu_Item *m = const_cast<Fl_Menu_Item*> (&(menubar->menu ()[t]));
00317           if (m->submenu ())
00318             {
00319               // item has submenu
00320               if (!menupath.empty ())
00321                 menupath += "/";
00322               menupath += m->label ();
00323 
00324               if (menupath.compare (findname) == 0 )
00325                 return (t);
00326             }
00327           else
00328             {
00329               // End of submenu? Pop back one level.
00330               if (m->label () == NULL)
00331                 {
00332                   std::size_t idx = menupath.find_last_of ("/");
00333                   if (idx != std::string::npos)
00334                     menupath.erase (idx);
00335                   else
00336                     menupath.clear ();
00337                   continue;
00338                 }
00339               // Menu item?
00340               std::string itempath = menupath;
00341               if (!itempath.empty ())
00342                 itempath += "/";
00343               itempath += m->label ();
00344 
00345               if (itempath.compare (findname) == 0)
00346                 return (t);
00347             }
00348         }
00349       return (-1);
00350     }
00351 
00352   Matrix find_uimenu_children (uimenu::properties& uimenup) const
00353     {
00354       Matrix uimenu_childs = uimenup.get_all_children ();
00355       Matrix retval = do_find_uimenu_children (uimenu_childs);
00356       return retval;
00357     }
00358 
00359   Matrix find_uimenu_children (figure::properties& figp) const
00360     {
00361       Matrix uimenu_childs = figp.get_all_children ();
00362       Matrix retval = do_find_uimenu_children (uimenu_childs);
00363       return retval;
00364     }
00365 
00366   Matrix do_find_uimenu_children (Matrix uimenu_childs) const
00367     {
00368       octave_idx_type k = 0;
00369 
00370 
00371       Matrix pos = Matrix (uimenu_childs.numel (), 1);
00372 
00373       for (octave_idx_type ii = 0; ii < uimenu_childs.numel (); ii++)
00374       {
00375         graphics_object kidgo = gh_manager::get_object (uimenu_childs (ii));
00376 
00377         if (kidgo.valid_object () && kidgo.isa ("uimenu"))
00378           {
00379             uimenu_childs(k) = uimenu_childs(ii);
00380             pos(k++) =
00381               dynamic_cast<uimenu::properties&> (kidgo.get_properties ()).get_position ();
00382           }
00383       }
00384 
00385       uimenu_childs.resize (k, 1);
00386       pos.resize (k, 1);
00387       Matrix retval = Matrix (k, 1);
00388       // Don't know if this is the best method to sort.
00389       // Can we avoid the for loop?
00390       Array<octave_idx_type> sidx = pos.sort_rows_idx (DESCENDING);
00391       for (octave_idx_type ii = 0; ii < k; ii++)
00392         retval(ii) = uimenu_childs (sidx(ii));
00393 
00394       return retval;
00395     }
00396 
00397   void delete_entry (uimenu::properties& uimenup)
00398     {
00399       std::string fltk_label = uimenup.get_fltk_label ();
00400       int idx = find_index_by_name (fltk_label.c_str ());
00401 
00402       if (idx >= 0)
00403         menubar->remove (idx);
00404     }
00405 
00406   void update_accelerator (uimenu::properties& uimenup)
00407     {
00408       std::string fltk_label = uimenup.get_fltk_label ();
00409       if (!fltk_label.empty ())
00410         {
00411           Fl_Menu_Item* item = const_cast<Fl_Menu_Item*> (menubar->find_item (fltk_label.c_str ()));
00412           if (item != NULL)
00413             {
00414               std::string acc = uimenup.get_accelerator ();
00415               if (acc.length () > 0)
00416                 {
00417                   int key = FL_CTRL + acc[0];
00418                   item->shortcut (key);
00419                 }
00420             }
00421         }
00422     }
00423 
00424   void update_callback (uimenu::properties& uimenup)
00425     {
00426       std::string fltk_label = uimenup.get_fltk_label ();
00427       if (!fltk_label.empty ())
00428         {
00429           Fl_Menu_Item* item = const_cast<Fl_Menu_Item*> (menubar->find_item (fltk_label.c_str ()));
00430           if (item != NULL)
00431             {
00432               if (!uimenup.get_callback ().is_empty ())
00433                 item->callback (static_cast<Fl_Callback*> (script_cb),
00434                                 static_cast<void*> (&uimenup));
00435               else
00436                 item->callback (NULL, static_cast<void*> (0));
00437             }
00438         }
00439     }
00440 
00441   void update_enable (uimenu::properties& uimenup)
00442     {
00443       std::string fltk_label = uimenup.get_fltk_label ();
00444       if (!fltk_label.empty ())
00445         {
00446           Fl_Menu_Item* item = const_cast<Fl_Menu_Item*> (menubar->find_item (fltk_label.c_str ()));
00447           if (item != NULL)
00448             {
00449               if (uimenup.is_enable ())
00450                 item->activate ();
00451               else
00452                 item->deactivate ();
00453             }
00454         }
00455     }
00456 
00457   void update_foregroundcolor (uimenu::properties& uimenup)
00458     {
00459       std::string fltk_label = uimenup.get_fltk_label ();
00460       if (!fltk_label.empty ())
00461         {
00462           Fl_Menu_Item* item = const_cast<Fl_Menu_Item*> (menubar->find_item (fltk_label.c_str ()));
00463           if (item != NULL)
00464             {
00465               Matrix rgb = uimenup.get_foregroundcolor_rgb ();
00466 
00467               uchar r = static_cast<uchar> (gnulib::floor (rgb (0) * 255));
00468               uchar g = static_cast<uchar> (gnulib::floor (rgb (1) * 255));
00469               uchar b = static_cast<uchar> (gnulib::floor (rgb (2) * 255));
00470 
00471               item->labelcolor (fl_rgb_color (r, g, b));
00472             }
00473         }
00474     }
00475 
00476   void update_seperator (const uimenu::properties& uimenup)
00477     {
00478       // Matlab places the separator before the current
00479       // menu entry, while fltk places it after. So we need to find
00480       // the previous item in this menu/submenu. (Kai)
00481       std::string fltk_label = uimenup.get_fltk_label ();
00482       if (!fltk_label.empty ())
00483         {
00484           int itemflags = 0, idx;
00485           int curr_idx = find_index_by_name (fltk_label.c_str ());
00486 
00487           for (idx = curr_idx - 1; idx >= 0; idx--)
00488             {
00489               Fl_Menu_Item* item = const_cast<Fl_Menu_Item*> (&menubar->menu () [idx]);
00490               itemflags = item->flags;
00491               if (item->label () != NULL)
00492                 break;
00493             }
00494 
00495           if (idx >= 0 && idx < menubar->size ())
00496             {
00497               if (uimenup.is_separator ())
00498                 {
00499                   if (idx >= 0 && !(itemflags & FL_SUBMENU))
00500                     menubar->mode (idx, itemflags | FL_MENU_DIVIDER);
00501                 }
00502               else
00503                 menubar->mode (idx, itemflags & (~FL_MENU_DIVIDER));
00504             }
00505         }
00506     }
00507 
00508   void update_visible (uimenu::properties& uimenup)
00509     {
00510       std::string fltk_label = uimenup.get_fltk_label ();
00511       if (!fltk_label.empty ())
00512         {
00513           Fl_Menu_Item* item
00514             = const_cast<Fl_Menu_Item*> (menubar->find_item (fltk_label.c_str ()));
00515           if (item != NULL)
00516             {
00517               if (uimenup.is_visible ())
00518                 item->show ();
00519               else
00520                 item->hide ();
00521             }
00522         }
00523     }
00524 
00525   void add_entry (uimenu::properties& uimenup)
00526     {
00527 
00528       std::string fltk_label = uimenup.get_fltk_label ();
00529 
00530       if (!fltk_label.empty ())
00531         {
00532           bool item_added = false;
00533           do
00534             {
00535               const Fl_Menu_Item* item
00536                 = menubar->find_item (fltk_label.c_str ());
00537 
00538               if (item == NULL)
00539                 {
00540                   Matrix uimenu_ch = find_uimenu_children (uimenup);
00541                   int len = uimenu_ch.numel ();
00542                   int flags = 0;
00543                   if (len > 0)
00544                     flags = FL_SUBMENU;
00545                   if (len == 0 && uimenup.is_checked ())
00546                     flags += FL_MENU_TOGGLE + FL_MENU_VALUE;
00547                   menubar->add (fltk_label.c_str (), 0, 0, 0, flags);
00548                   item_added = true;
00549                 }
00550               else
00551                 {
00552                   //avoid duplicate menulabels
00553                   std::size_t idx1 = fltk_label.find_last_of ("(");
00554                   std::size_t idx2 = fltk_label.find_last_of (")");
00555                   int len = idx2 - idx1;
00556                   int val = 1;
00557                   if (len > 0)
00558                     {
00559                       std::string valstr = fltk_label.substr (idx1 + 1, len - 1);
00560                       fltk_label.erase (idx1, len + 1);
00561                       val = atoi (valstr.c_str ());
00562                       if (val > 0 && val < 99)
00563                         val++;
00564                     }
00565                   std::ostringstream valstream;
00566                   valstream << val;
00567                   fltk_label += "(" + valstream.str () + ")";
00568                 }
00569             }
00570           while (!item_added);
00571           uimenup.set_fltk_label (fltk_label);
00572         }
00573     }
00574 
00575   void add_to_menu (uimenu::properties& uimenup)
00576     {
00577       Matrix kids = find_uimenu_children (uimenup);
00578       int len = kids.length ();
00579       std::string fltk_label = uimenup.get_fltk_label ();
00580 
00581       add_entry (uimenup);
00582       update_foregroundcolor (uimenup);
00583       update_callback (uimenup);
00584       update_accelerator (uimenup);
00585       update_enable (uimenup);
00586       update_visible (uimenup);
00587       update_seperator (uimenup);
00588 
00589       for (octave_idx_type ii = 0; ii < len; ii++)
00590         {
00591           graphics_object kgo = gh_manager::get_object (kids (len - (ii + 1)));
00592           if (kgo.valid_object ())
00593             {
00594               uimenu::properties& kprop = dynamic_cast<uimenu::properties&> (kgo.get_properties ());
00595               add_to_menu (kprop);
00596             }
00597         }
00598     }
00599 
00600   void add_to_menu (figure::properties& figp)
00601     {
00602       Matrix kids = find_uimenu_children (figp);
00603       int len = kids.length ();
00604       menubar->clear ();
00605       for (octave_idx_type ii = 0; ii < len; ii++)
00606         {
00607           graphics_object kgo = gh_manager::get_object (kids (len - (ii + 1)));
00608 
00609           if (kgo.valid_object ())
00610             {
00611               uimenu::properties& kprop = dynamic_cast<uimenu::properties&> (kgo.get_properties ());
00612               add_to_menu (kprop);
00613             }
00614         }
00615     }
00616 
00617   template <class T_prop>
00618   void remove_from_menu (T_prop& prop)
00619     {
00620       Matrix kids;
00621       std::string type = prop.get_type ();
00622       kids = find_uimenu_children (prop);
00623       int len = kids.length ();
00624 
00625       for (octave_idx_type ii = 0; ii < len; ii++)
00626         {
00627           graphics_object kgo = gh_manager::get_object (kids (len - (ii + 1)));
00628 
00629           if (kgo.valid_object ())
00630             {
00631               uimenu::properties kprop = dynamic_cast<uimenu::properties&> (kgo.get_properties ());
00632               remove_from_menu (kprop);
00633             }
00634         }
00635 
00636       if (type.compare ("uimenu") == 0)
00637         delete_entry (dynamic_cast<uimenu::properties&> (prop));
00638       else if (type.compare ("figure") == 0)
00639         menubar->clear ();
00640     }
00641 
00642   ~fltk_uimenu (void)
00643     {
00644       delete menubar;
00645     }
00646 
00647 private:
00648 
00649   // No copying!
00650 
00651   fltk_uimenu (const fltk_uimenu&);
00652 
00653   fltk_uimenu operator = (const fltk_uimenu&);
00654 
00655   Fl_Menu_Bar* menubar;
00656 };
00657 
00658 class plot_window : public Fl_Window
00659 {
00660   friend class fltk_uimenu;
00661 public:
00662   plot_window (int xx, int yy, int ww, int hh, figure::properties& xfp)
00663     : Fl_Window (xx, yy, ww, hh, "octave"), window_label (), shift (0),
00664       ndim (2), fp (xfp), canvas (0), autoscale (0), togglegrid (0),
00665       panzoom (0), rotate (0), help (0), status (0),
00666       ax_obj (), pos_x (0), pos_y (0)
00667   {
00668     callback (window_close, static_cast<void*> (this));
00669     size_range (4*status_h, 2*status_h);
00670 
00671     begin ();
00672     {
00673 
00674       canvas = new OpenGL_fltk (0, 0, ww, hh - status_h, number ());
00675 
00676       uimenu = new fltk_uimenu (0, 0, ww, menu_h);
00677       uimenu->hide ();
00678 
00679       bottom = new Fl_Box (0, hh - status_h, ww, status_h);
00680       bottom->box(FL_FLAT_BOX);
00681 
00682       ndim = calc_dimensions (gh_manager::get_object (fp.get___myhandle__ ()));
00683 
00684       autoscale = new Fl_Button (0, hh - status_h, status_h, status_h, "A");
00685       autoscale->callback (button_callback, static_cast<void*> (this));
00686       autoscale->tooltip ("Autoscale");
00687 
00688       togglegrid = new Fl_Button (status_h, hh - status_h, status_h,
00689                                   status_h, "G");
00690       togglegrid->callback (button_callback, static_cast<void*> (this));
00691       togglegrid->tooltip ("Toggle Grid");
00692 
00693       panzoom = new Fl_Button (2 * status_h, hh - status_h, status_h,
00694                                status_h, "P");
00695       panzoom->callback (button_callback, static_cast<void*> (this));
00696       panzoom->tooltip ("Mouse Pan/Zoom");
00697 
00698       rotate = new Fl_Button (3 * status_h, hh - status_h, status_h,
00699                               status_h, "R");
00700       rotate->callback (button_callback, static_cast<void*> (this));
00701       rotate->tooltip ("Mouse Rotate");
00702 
00703       if (ndim == 2)
00704         rotate->deactivate ();
00705 
00706       help = new Fl_Button (4 * status_h, hh - status_h, status_h,
00707                             status_h, "?");
00708       help->callback (button_callback, static_cast<void*> (this));
00709       help->tooltip ("Help");
00710 
00711       status = new Fl_Output (5 * status_h, hh - status_h,
00712                               ww > 2*status_h ? ww - status_h : 0,
00713                               status_h, "");
00714 
00715       status->textcolor (FL_BLACK);
00716       status->color (FL_GRAY);
00717       status->textfont (FL_COURIER);
00718       status->textsize (10);
00719       status->box (FL_ENGRAVED_BOX);
00720 
00721       // This allows us to have a valid OpenGL context right away.
00722       canvas->mode (FL_DEPTH | FL_DOUBLE );
00723       if (fp.is_visible ())
00724         {
00725           show ();
00726           if (fp.get_currentaxes ().ok())
00727             show_canvas ();
00728           else
00729             hide_canvas ();
00730         }
00731     }
00732     end ();
00733 
00734     status->show ();
00735     autoscale->show ();
00736     togglegrid->show ();
00737     panzoom->show ();
00738     rotate->show ();
00739 
00740     set_name ();
00741     resizable (canvas);
00742     gui_mode = (ndim == 3 ? rotate_zoom : pan_zoom);
00743     uimenu->add_to_menu (fp);
00744     if (uimenu->items_to_show ())
00745       show_menubar ();
00746     else
00747       hide_menubar ();
00748   }
00749 
00750   ~plot_window (void)
00751   {
00752     canvas->hide ();
00753     status->hide ();
00754     uimenu->hide ();
00755     this->hide ();
00756   }
00757 
00758   double number (void) { return fp.get___myhandle__ ().value (); }
00759 
00760   void renumber (double new_number)
00761   {
00762     if (canvas)
00763       {
00764         if (canvas->renumber (new_number))
00765           mark_modified ();
00766       }
00767     else
00768       error ("unable to renumber figure");
00769   }
00770 
00771   void print (const std::string& cmd, const std::string& term)
00772   {
00773     canvas->print (cmd, term);
00774 
00775     // Print immediately so the output file will exist when the drawnow
00776     // command is done.
00777     mark_modified ();
00778     Fl::wait (fltk_maxtime);
00779   }
00780 
00781   void show_menubar (void)
00782   {
00783     if (!uimenu->is_visible ())
00784       {
00785         canvas->resize (canvas->x (),
00786                         canvas->y () + menu_h,
00787                         canvas->w (),
00788                         canvas->h () - menu_h);
00789         uimenu->show ();
00790         mark_modified ();
00791       }
00792   }
00793 
00794   void hide_menubar (void)
00795   {
00796     if (uimenu->is_visible ())
00797       {
00798         canvas->resize (canvas->x (),
00799                         canvas->y () - menu_h,
00800                         canvas->w (),
00801                         canvas->h () + menu_h);
00802         uimenu->hide ();
00803         mark_modified ();
00804       }
00805   }
00806 
00807   void uimenu_update (const graphics_handle& gh, int id)
00808   {
00809     graphics_object uimenu_obj = gh_manager::get_object (gh);
00810 
00811     if (uimenu_obj.valid_object () && uimenu_obj.isa ("uimenu"))
00812       {
00813         uimenu::properties& uimenup =
00814           dynamic_cast<uimenu::properties&> (uimenu_obj.get_properties ());
00815         std::string fltk_label = uimenup.get_fltk_label ();
00816         graphics_object fig = uimenu_obj.get_ancestor ("figure");
00817         figure::properties& figp =
00818           dynamic_cast<figure::properties&> (fig.get_properties ());
00819 
00820         switch (id)
00821           {
00822           case base_properties::ID_BEINGDELETED:
00823             uimenu->remove_from_menu (uimenup);
00824             break;
00825 
00826           case base_properties::ID_VISIBLE:
00827             uimenu->update_visible (uimenup);
00828             break;
00829 
00830           case uimenu::properties::ID_ACCELERATOR:
00831             uimenu->update_accelerator (uimenup);
00832             break;
00833 
00834           case uimenu::properties::ID_CALLBACK:
00835             uimenu->update_callback (uimenup);
00836             break;
00837 
00838           case uimenu::properties::ID_CHECKED:
00839             uimenu->add_to_menu (figp);//rebuilding entire menu
00840             break;
00841 
00842           case uimenu::properties::ID_ENABLE:
00843             uimenu->update_enable (uimenup);
00844             break;
00845 
00846           case uimenu::properties::ID_FOREGROUNDCOLOR:
00847             uimenu->update_foregroundcolor (uimenup);
00848             break;
00849 
00850           case uimenu::properties::ID_LABEL:
00851             uimenu->add_to_menu (figp);//rebuilding entire menu
00852             break;
00853 
00854           case uimenu::properties::ID_POSITION:
00855             uimenu->add_to_menu (figp);//rebuilding entire menu
00856             break;
00857 
00858           case uimenu::properties::ID_SEPARATOR:
00859             uimenu->update_seperator (uimenup);
00860             break;
00861           }
00862 
00863         if (uimenu->items_to_show ())
00864           show_menubar ();
00865         else
00866           hide_menubar ();
00867 
00868         mark_modified();
00869       }
00870   }
00871 
00872   void show_canvas (void)
00873   {
00874     if (fp.is_visible ())
00875       {
00876         canvas->show ();
00877         canvas->make_current ();
00878       }
00879   }
00880 
00881   void hide_canvas (void)
00882   {
00883     canvas->hide ();
00884   }
00885 
00886   void mark_modified (void)
00887   {
00888     damage (FL_DAMAGE_ALL);
00889     canvas->damage (FL_DAMAGE_ALL);
00890     ndim = calc_dimensions (gh_manager::get_object (fp.get___myhandle__ ()));
00891 
00892     if (ndim == 3)
00893       rotate->activate ();
00894     else if (ndim == 2 && gui_mode == rotate_zoom)
00895       {
00896         rotate->deactivate ();
00897         gui_mode = pan_zoom;
00898       }
00899   }
00900 
00901   void set_name (void)
00902   {
00903     window_label = fp.get_title ();
00904     label (window_label.c_str ());
00905   }
00906 
00907 private:
00908 
00909   // No copying!
00910 
00911   plot_window (const plot_window&);
00912 
00913   plot_window& operator = (const plot_window&);
00914 
00915   // window name -- this must exists for the duration of the window's
00916   // life
00917   std::string window_label;
00918 
00919   // Mod keys status
00920   int shift;
00921 
00922   // Number of dimensions, 2 or 3.
00923   int ndim;
00924 
00925   // Figure properties.
00926   figure::properties& fp;
00927 
00928   // Status area height.
00929   static const int status_h = 20;
00930 
00931   // Menu height
00932   static const int menu_h = 20;
00933 
00934   // Window callback.
00935   static void window_close (Fl_Widget*, void* data)
00936   {
00937     octave_value_list args;
00938     args(0) = static_cast<plot_window*> (data)->number ();
00939     feval ("close", args);
00940   }
00941 
00942   // Button callbacks.
00943   static void button_callback (Fl_Widget* ww, void* data)
00944   {
00945     static_cast<plot_window*> (data)->button_press (ww, data);
00946   }
00947 
00948   void button_press (Fl_Widget* widg, void*)
00949   {
00950     if (widg == autoscale)
00951       axis_auto ();
00952 
00953     if (widg == togglegrid)
00954       toggle_grid ();
00955 
00956     if (widg == panzoom)
00957       gui_mode = pan_zoom;
00958 
00959     if (widg == rotate && ndim == 3)
00960       gui_mode = rotate_zoom;
00961 
00962     if (widg == help)
00963       fl_message ("%s", help_text);
00964   }
00965 
00966   fltk_uimenu* uimenu;
00967   OpenGL_fltk* canvas;
00968   Fl_Box*    bottom;
00969   Fl_Button* autoscale;
00970   Fl_Button* togglegrid;
00971   Fl_Button* panzoom;
00972   Fl_Button* rotate;
00973   Fl_Button* help;
00974   Fl_Output* status;
00975   graphics_object ax_obj;
00976   int pos_x;
00977   int pos_y;
00978 
00979   void axis_auto (void)
00980   {
00981     octave_value_list args;
00982     args(0) = fp.get_currentaxes ().as_octave_value ();
00983     args(1) = "auto";
00984     feval ("axis", args);
00985     mark_modified ();
00986   }
00987 
00988   void toggle_grid (void)
00989   {
00990     octave_value_list args;
00991     if (fp.get_currentaxes ().ok ())
00992       args(0) = fp.get_currentaxes ().as_octave_value ();
00993 
00994     feval ("grid", args);
00995     mark_modified ();
00996   }
00997 
00998   void pixel2pos (const graphics_handle& ax, int px, int py, double& xx,
00999                   double& yy) const
01000   {
01001     pixel2pos ( gh_manager::get_object (ax), px, py, xx, yy);
01002   }
01003 
01004   void pixel2pos (graphics_object ax, int px, int py, double& xx,
01005                   double& yy) const
01006   {
01007     if (ax && ax.isa ("axes"))
01008       {
01009         axes::properties& ap =
01010           dynamic_cast<axes::properties&> (ax.get_properties ());
01011         ColumnVector pp = ap.pixel2coord (px, py);
01012         xx = pp(0);
01013         yy = pp(1);
01014       }
01015   }
01016 
01017   graphics_handle pixel2axes_or_ca (int px, int py )
01018   {
01019     Matrix kids = fp.get_children ();
01020     int len = kids.length ();
01021 
01022     for (int k = 0; k < len; k++)
01023       {
01024         graphics_handle hnd = gh_manager::lookup (kids(k));
01025 
01026         if (hnd.ok ())
01027           {
01028             graphics_object kid = gh_manager::get_object (hnd);
01029 
01030             if (kid.valid_object () && kid.isa ("axes"))
01031               {
01032                 Matrix bb = kid.get_properties ().get_boundingbox (true);
01033 
01034                 if (bb(0) <= px && px < (bb(0)+bb(2))
01035                     && bb(1) <= py && py < (bb(1)+bb(3)))
01036                   {
01037                     return hnd;
01038                   }
01039               }
01040           }
01041       }
01042     return fp.get_currentaxes ();
01043   }
01044 
01045   void pixel2status (const graphics_handle& ax, int px0, int py0,
01046                      int px1 = -1, int py1 = -1)
01047   {
01048     pixel2status (gh_manager::get_object (ax), px0, py0, px1, py1);
01049   }
01050 
01051   void pixel2status (graphics_object ax, int px0, int py0,
01052                      int px1 = -1, int py1 = -1)
01053   {
01054     double x0, y0, x1, y1;
01055     std::stringstream cbuf;
01056     cbuf.precision (4);
01057     cbuf.width (6);
01058     pixel2pos (ax, px0, py0, x0, y0);
01059     cbuf << "[" << x0 << ", " << y0 << "]";
01060     if (px1 >= 0)
01061       {
01062         pixel2pos (ax, px1, py1, x1, y1);
01063         cbuf << " -> ["<< x1 << ", " << y1 << "]";
01064       }
01065 
01066     status->value (cbuf.str ().c_str ());
01067     status->redraw ();
01068   }
01069 
01070   void view2status (graphics_object ax)
01071   {
01072      if (ax && ax.isa ("axes"))
01073        {
01074          axes::properties& ap =
01075            dynamic_cast<axes::properties&> (ax.get_properties ());
01076          std::stringstream cbuf;
01077          cbuf.precision (4);
01078          cbuf.width (6);
01079          Matrix v (1,2,0);
01080          v = ap.get ("view").matrix_value ();
01081          cbuf << "[azimuth: " << v(0) << ", elevation: " << v(1) << "]";
01082 
01083          status->value (cbuf.str ().c_str ());
01084          status->redraw ();
01085        }
01086   }
01087 
01088   void set_currentpoint (int px, int py)
01089   {
01090     if (!fp.is_beingdeleted ())
01091       {
01092         Matrix pos (1,2,0);
01093         pos(0) = px;
01094         pos(1) = h () - status_h - menu_h - py;
01095         fp.set_currentpoint (pos);
01096       }
01097   }
01098 
01099   void set_axes_currentpoint (graphics_object ax, int px, int py)
01100   {
01101     if (ax.valid_object ())
01102       {
01103         axes::properties& ap =
01104           dynamic_cast<axes::properties&> (ax.get_properties ());
01105 
01106         double xx, yy;
01107         pixel2pos (ax, px, py, xx, yy);
01108 
01109         Matrix pos (2,3,0);
01110         pos(0,0) = xx;
01111         pos(1,0) = yy;
01112         pos(0,1) = xx;
01113         pos(1,1) = yy;
01114 
01115         ap.set_currentpoint (pos);
01116       }
01117   }
01118 
01119   int key2shift (int key)
01120   {
01121     if (key == FL_Shift_L || key == FL_Shift_R)
01122       return FL_SHIFT;
01123 
01124     if (key == FL_Control_L || key == FL_Control_R)
01125       return FL_CTRL;
01126 
01127     if (key == FL_Alt_L || key == FL_Alt_R)
01128       return FL_ALT;
01129 
01130     if (key == FL_Meta_L || key == FL_Meta_R)
01131       return FL_META;
01132 
01133     return 0;
01134   }
01135 
01136   int key2ascii (int key)
01137   {
01138     if (key < 256) return key;
01139     if (key == FL_Tab) return '\t';
01140     if (key == FL_Enter) return 0x0a;
01141     if (key == FL_BackSpace) return 0x08;
01142     if (key == FL_Escape) return 0x1b;
01143 
01144     return 0;
01145   }
01146 
01147   Cell modifier2cell ()
01148   {
01149     string_vector mod;
01150 
01151     if (shift & FL_SHIFT)
01152       mod.append (std::string ("shift"));
01153     if (shift & FL_CTRL)
01154       mod.append (std::string ("control"));
01155     if (shift & FL_ALT || shift & FL_META)
01156       mod.append (std::string ("alt"));
01157 
01158     return Cell (mod);
01159   }
01160 
01161   void resize (int xx,int yy,int ww,int hh)
01162   {
01163     Fl_Window::resize (xx, yy, ww, hh);
01164 
01165     Matrix pos (1,4,0);
01166     pos(0) = xx;
01167     pos(1) = yy;
01168     pos(2) = ww;
01169     pos(3) = hh - status_h - menu_h;
01170 
01171     fp.set_position (pos);
01172   }
01173 
01174   void draw (void)
01175   {
01176     Matrix pos = fp.get_position ().matrix_value ();
01177     Fl_Window::resize (pos(0), pos(1), pos(2), pos(3) + status_h + menu_h);
01178 
01179     return Fl_Window::draw ();
01180   }
01181 
01182   int handle (int event)
01183   {
01184     graphics_handle gh;
01185 
01186     graphics_object fig = gh_manager::get_object (fp.get___myhandle__ ());
01187     int retval = Fl_Window::handle (event);
01188 
01189     // We only handle events which are in the canvas area.
01190     if (!Fl::event_inside (canvas))
01191       return retval;
01192 
01193     if (!fp.is_beingdeleted ())
01194       {
01195         switch (event)
01196           {
01197           case FL_KEYDOWN:
01198             {
01199               int key = Fl::event_key ();
01200 
01201               shift |= key2shift (key);
01202               int key_a = key2ascii (key);
01203               if (key_a && fp.get_keypressfcn ().is_defined ())
01204                 {
01205                   Octave_map evt;
01206                   evt.assign ("Character", octave_value (key_a));
01207                   evt.assign ("Key", octave_value (std::tolower (key_a)));
01208                   evt.assign ("Modifier", octave_value (modifier2cell ()));
01209                   fp.execute_keypressfcn (evt);
01210                 }
01211               switch (key)
01212                 {
01213                 case 'a':
01214                 case 'A':
01215                   axis_auto ();
01216                 break;
01217 
01218                 case 'g':
01219                 case 'G':
01220                   toggle_grid ();
01221                 break;
01222 
01223                 case 'p':
01224                 case 'P':
01225                   gui_mode = pan_zoom;
01226                 break;
01227 
01228                 case 'r':
01229                 case 'R':
01230                   gui_mode = rotate_zoom;
01231                 break;
01232                 }
01233             }
01234             break;
01235 
01236           case FL_KEYUP:
01237             {
01238               int key = Fl::event_key ();
01239 
01240               shift &= (~key2shift (key));
01241               int key_a = key2ascii (key);
01242               if (key_a && fp.get_keyreleasefcn ().is_defined ())
01243                 {
01244                   Octave_map evt;
01245                   evt.assign ("Character", octave_value (key_a));
01246                   evt.assign ("Key", octave_value (std::tolower (key_a)));
01247                   evt.assign ("Modifier", octave_value (modifier2cell ()));
01248                   fp.execute_keyreleasefcn (evt);
01249                 }
01250             }
01251             break;
01252 
01253           case FL_MOVE:
01254             pixel2status (pixel2axes_or_ca (Fl::event_x (), Fl::event_y ()),
01255                           Fl::event_x (), Fl::event_y ());
01256             break;
01257 
01258           case FL_PUSH:
01259             pos_x = Fl::event_x ();
01260             pos_y = Fl::event_y ();
01261 
01262             set_currentpoint (Fl::event_x (), Fl::event_y ());
01263 
01264             gh = pixel2axes_or_ca (pos_x, pos_y);
01265 
01266             if (gh.ok ())
01267               {
01268                 ax_obj = gh_manager::get_object (gh);
01269                 set_axes_currentpoint (ax_obj, pos_x, pos_y);
01270               }
01271 
01272             fp.execute_windowbuttondownfcn ();
01273 
01274             if (Fl::event_button () == 1 || Fl::event_button () == 3)
01275               return 1;
01276 
01277             break;
01278 
01279           case FL_DRAG:
01280             if (fp.get_windowbuttonmotionfcn ().is_defined ())
01281               {
01282                 set_currentpoint (Fl::event_x (), Fl::event_y ());
01283                 fp.execute_windowbuttonmotionfcn ();
01284               }
01285 
01286             if (Fl::event_button () == 1)
01287               {
01288                 if (ax_obj && ax_obj.isa ("axes"))
01289                   {
01290                     if (gui_mode == pan_zoom)
01291                       pixel2status (ax_obj, pos_x, pos_y,
01292                                     Fl::event_x (), Fl::event_y ());
01293                     else
01294                       view2status (ax_obj);
01295                     axes::properties& ap =
01296                       dynamic_cast<axes::properties&> (ax_obj.get_properties ());
01297 
01298                     double x0, y0, x1, y1;
01299                     Matrix pos = fp.get_position ().matrix_value ();
01300                     pixel2pos (ax_obj, pos_x, pos_y, x0, y0);
01301                     pixel2pos (ax_obj, Fl::event_x (), Fl::event_y (), x1, y1);
01302 
01303                     if (gui_mode == pan_zoom)
01304                       ap.translate_view (x0 - x1, y0 - y1);
01305                     else if (gui_mode == rotate_zoom)
01306                       {
01307                         double daz, del;
01308                         daz = (Fl::event_x () - pos_x) / pos(2) * 360;
01309                         del = (Fl::event_y () - pos_y) / pos(3) * 360;
01310                         ap.rotate_view (del, daz);
01311                       }
01312 
01313                     pos_x = Fl::event_x ();
01314                     pos_y = Fl::event_y ();
01315                     mark_modified ();
01316                   }
01317                 return 1;
01318               }
01319             else if (Fl::event_button () == 3)
01320               {
01321                 pixel2status (ax_obj, pos_x, pos_y,
01322                               Fl::event_x (), Fl::event_y ());
01323                 Matrix zoom_box (1,4,0);
01324                 zoom_box (0) = pos_x;
01325                 zoom_box (1) = pos_y;
01326                 zoom_box (2) =  Fl::event_x ();
01327                 zoom_box (3) =  Fl::event_y ();
01328                 canvas->set_zoom_box (zoom_box);
01329                 canvas->zoom (true);
01330                 canvas->redraw ();
01331               }
01332 
01333             break;
01334 
01335           case FL_MOUSEWHEEL:
01336             {
01337               graphics_object ax =
01338                 gh_manager::get_object (pixel2axes_or_ca (Fl::event_x (),
01339                                                           Fl::event_y ()));
01340               if (ax && ax.isa ("axes"))
01341                 {
01342                   axes::properties& ap =
01343                     dynamic_cast<axes::properties&> (ax.get_properties ());
01344 
01345                   // Determine if we're zooming in or out.
01346                   const double factor =
01347                     (Fl::event_dy () > 0) ? 1.0 + wheel_zoom_speed : 1.0 - wheel_zoom_speed;
01348 
01349                   // Get the point we're zooming about.
01350                   double x1, y1;
01351                   pixel2pos (ax, Fl::event_x (), Fl::event_y (), x1, y1);
01352 
01353                   ap.zoom_about_point (x1, y1, factor, false);
01354                   mark_modified ();
01355                 }
01356             }
01357           return 1;
01358 
01359           case FL_RELEASE:
01360             if (fp.get_windowbuttonupfcn ().is_defined ())
01361               {
01362                 set_currentpoint (Fl::event_x (), Fl::event_y ());
01363                 fp.execute_windowbuttonupfcn ();
01364               }
01365 
01366             if (Fl::event_button () == 1)
01367               {
01368                 if ( Fl::event_clicks () == 1)
01369                   {
01370                     if (ax_obj && ax_obj.isa ("axes"))
01371                       {
01372                         axes::properties& ap =
01373                           dynamic_cast<axes::properties&> (ax_obj.get_properties ());
01374                         ap.set_xlimmode ("auto");
01375                         ap.set_ylimmode ("auto");
01376                         ap.set_zlimmode ("auto");
01377                         mark_modified ();
01378                       }
01379                   }
01380               }
01381             if (Fl::event_button () == 3)
01382               {
01383                 // End of drag -- zoom.
01384                 if (canvas->zoom ())
01385                   {
01386                     canvas->zoom (false);
01387                     double x0,y0,x1,y1;
01388                     if (ax_obj && ax_obj.isa ("axes"))
01389                       {
01390                         axes::properties& ap =
01391                           dynamic_cast<axes::properties&> (ax_obj.get_properties ());
01392                         pixel2pos (ax_obj, pos_x, pos_y, x0, y0);
01393                         pixel2pos (ax_obj, Fl::event_x (), Fl::event_y (),
01394                                    x1, y1);
01395                         Matrix xl (1,2,0);
01396                         Matrix yl (1,2,0);
01397                         if (x0 < x1)
01398                           {
01399                             xl(0) = x0;
01400                             xl(1) = x1;
01401                           }
01402                         else
01403                           {
01404                             xl(0) = x1;
01405                             xl(1) = x0;
01406                           }
01407                         if (y0 < y1)
01408                           {
01409                             yl(0) = y0;
01410                             yl(1) = y1;
01411                           }
01412                         else
01413                           {
01414                             yl(0) = y1;
01415                             yl(1) = y0;
01416                           }
01417                         ap.zoom (xl, yl);
01418                         mark_modified ();
01419                       }
01420                   }
01421               }
01422             break;
01423           }
01424       }
01425 
01426     return retval;
01427   }
01428 };
01429 
01430 class figure_manager
01431 {
01432 public:
01433 
01434   static bool instance_ok (void)
01435   {
01436     bool retval = true;
01437 
01438     if (! instance)
01439       instance = new figure_manager ();
01440 
01441     if (! instance)
01442       {
01443         ::error ("unable to create figure_manager object!");
01444 
01445         retval = false;
01446       }
01447 
01448     return retval;
01449   }
01450 
01451   ~figure_manager (void)
01452   {
01453     close_all ();
01454   }
01455 
01456   static void close_all (void)
01457   {
01458     if (instance_ok ())
01459       instance->do_close_all ();
01460   }
01461 
01462   static void new_window (figure::properties& fp)
01463   {
01464     if (instance_ok ())
01465       instance->do_new_window (fp);
01466   }
01467 
01468   static void delete_window (int idx)
01469   {
01470     if (instance_ok ())
01471       instance->do_delete_window (idx);
01472   }
01473 
01474   static void delete_window (const std::string& idx_str)
01475   {
01476     delete_window (str2idx (idx_str));
01477   }
01478 
01479   static void renumber_figure (const std::string& idx_str, double new_number)
01480   {
01481     if (instance_ok ())
01482       instance->do_renumber_figure (str2idx (idx_str), new_number);
01483   }
01484 
01485   static void toggle_window_visibility (int idx, bool is_visible)
01486   {
01487     if (instance_ok ())
01488       instance->do_toggle_window_visibility (idx, is_visible);
01489   }
01490 
01491   static void toggle_window_visibility (const std::string& idx_str,
01492                                         bool is_visible)
01493   {
01494     toggle_window_visibility (str2idx (idx_str), is_visible);
01495   }
01496 
01497   static void mark_modified (int idx)
01498   {
01499     if (instance_ok ())
01500       instance->do_mark_modified (idx);
01501   }
01502 
01503   static void mark_modified (const graphics_handle& gh)
01504   {
01505     mark_modified (hnd2idx (gh));
01506   }
01507 
01508   static void set_name (int idx)
01509   {
01510     if (instance_ok ())
01511       instance->do_set_name (idx);
01512   }
01513 
01514   static void set_name (const std::string& idx_str)
01515   {
01516     set_name (str2idx (idx_str));
01517   }
01518 
01519   static Matrix get_size (int idx)
01520   {
01521     return instance_ok () ? instance->do_get_size (idx) : Matrix ();
01522   }
01523 
01524   static Matrix get_size (const graphics_handle& gh)
01525   {
01526     return get_size (hnd2idx (gh));
01527   }
01528 
01529   static void print (const graphics_handle& gh, const std::string& cmd,
01530                      const std::string& term)
01531   {
01532     if (instance_ok ())
01533       instance->do_print (hnd2idx (gh), cmd, term);
01534   }
01535 
01536   static void uimenu_update (const graphics_handle& figh,
01537                              const graphics_handle& uimenuh, int id)
01538   {
01539     if (instance_ok ())
01540       instance->do_uimenu_update (hnd2idx (figh), uimenuh, id);
01541   }
01542 
01543   static void update_canvas (const graphics_handle& gh,
01544                              const graphics_handle& ca)
01545   {
01546     if (instance_ok ())
01547       instance->do_update_canvas (hnd2idx (gh), ca);
01548   }
01549 
01550   static void toggle_menubar_visibility (int fig_idx, bool menubar_is_figure)
01551   {
01552     if (instance_ok ())
01553       instance->do_toggle_menubar_visibility (fig_idx, menubar_is_figure);
01554   }
01555 
01556   static void toggle_menubar_visibility (const std::string& fig_idx_str,
01557                                          bool menubar_is_figure)
01558   {
01559     toggle_menubar_visibility (str2idx (fig_idx_str), menubar_is_figure);
01560   }
01561 
01562 private:
01563 
01564   static figure_manager *instance;
01565 
01566   figure_manager (void) { }
01567 
01568   // No copying!
01569   figure_manager (const figure_manager&);
01570   figure_manager& operator = (const figure_manager&);
01571 
01572   // Singelton -- hide all of the above.
01573 
01574   static int curr_index;
01575   typedef std::map<int, plot_window*> window_map;
01576   typedef window_map::iterator wm_iterator;;
01577   window_map windows;
01578 
01579   static std::string fltk_idx_header;
01580 
01581   void do_close_all (void)
01582   {
01583     wm_iterator win;
01584     for (win = windows.begin (); win != windows.end (); win++)
01585       delete win->second;
01586     windows.clear ();
01587   }
01588 
01589   void do_new_window (figure::properties& fp)
01590   {
01591     int idx = figprops2idx (fp);
01592 
01593     if (idx >= 0 && windows.find (idx) == windows.end ())
01594       {
01595         Matrix pos = fp.get_boundingbox (true);
01596 
01597         int x = pos(0);
01598         int y = pos(1);
01599         int w = pos(2);
01600         int h = pos(3);
01601 
01602         idx2figprops (curr_index, fp);
01603 
01604         windows[curr_index++] = new plot_window (x, y, w, h, fp);
01605       }
01606   }
01607 
01608   void do_delete_window (int idx)
01609   {
01610     wm_iterator win = windows.find (idx);
01611 
01612     if (win != windows.end ())
01613       {
01614         delete win->second;
01615         windows.erase (win);
01616       }
01617   }
01618 
01619   void do_renumber_figure (int idx, double new_number)
01620   {
01621     wm_iterator win = windows.find (idx);
01622 
01623     if (win != windows.end ())
01624       win->second->renumber (new_number);
01625   }
01626 
01627   void do_toggle_window_visibility (int idx, bool is_visible)
01628   {
01629     wm_iterator win = windows.find (idx);
01630 
01631     if (win != windows.end ())
01632       {
01633         if (is_visible)
01634           win->second->show ();
01635         else
01636           win->second->hide ();
01637 
01638         win->second->redraw ();
01639       }
01640   }
01641 
01642   void do_toggle_menubar_visibility (int fig_idx, bool menubar_is_figure)
01643   {
01644     wm_iterator win = windows.find (fig_idx);
01645 
01646     if (win != windows.end ())
01647       {
01648         if (menubar_is_figure)
01649           win->second->show_menubar ();
01650         else
01651           win->second->hide_menubar ();
01652 
01653         win->second->redraw ();
01654       }
01655   }
01656 
01657   void do_mark_modified (int idx)
01658   {
01659     wm_iterator win = windows.find (idx);
01660 
01661     if (win != windows.end ())
01662       win->second->mark_modified ();
01663   }
01664 
01665   void do_set_name (int idx)
01666   {
01667     wm_iterator win = windows.find (idx);
01668 
01669     if (win != windows.end ())
01670       win->second->set_name ();
01671   }
01672 
01673   Matrix do_get_size (int idx)
01674   {
01675     Matrix sz (1, 2, 0.0);
01676 
01677     wm_iterator win = windows.find (idx);
01678 
01679     if (win != windows.end ())
01680       {
01681         sz(0) = win->second->w ();
01682         sz(1) = win->second->h ();
01683       }
01684 
01685     return sz;
01686   }
01687 
01688   void do_print (int idx, const std::string& cmd, const std::string& term)
01689   {
01690     wm_iterator win = windows.find (idx);
01691 
01692     if (win != windows.end ())
01693       win->second->print (cmd, term);
01694   }
01695 
01696   void do_uimenu_update (int idx, const graphics_handle& gh, int id)
01697   {
01698     wm_iterator win = windows.find (idx);
01699 
01700     if (win != windows.end ())
01701       win->second->uimenu_update (gh, id);
01702   }
01703 
01704   void do_update_canvas (int idx, const graphics_handle& ca)
01705   {
01706     wm_iterator win = windows.find (idx);
01707 
01708     if (win != windows.end ())
01709       {
01710         if (ca.ok ())
01711           win->second->show_canvas ();
01712         else
01713           win->second->hide_canvas ();
01714       }
01715   }
01716 
01717   static int str2idx (const caseless_str& clstr)
01718   {
01719     int ind;
01720     if (clstr.find (fltk_idx_header,0) == 0)
01721       {
01722         std::istringstream istr (clstr.substr (fltk_idx_header.size ()));
01723         if (istr >> ind)
01724           return ind;
01725       }
01726     error ("figure_manager: could not recognize fltk index");
01727     return -1;
01728   }
01729 
01730   void idx2figprops (int idx, figure::properties& fp)
01731   {
01732     std::ostringstream ind_str;
01733     ind_str << fltk_idx_header << idx;
01734     fp.set___plot_stream__ (ind_str.str ());
01735   }
01736 
01737   static int figprops2idx (const figure::properties& fp)
01738   {
01739     if (fp.get___graphics_toolkit__ () == FLTK_GRAPHICS_TOOLKIT_NAME)
01740       {
01741         octave_value ps = fp.get___plot_stream__ ();
01742         if (ps.is_string ())
01743           return str2idx (ps.string_value ());
01744         else
01745           return 0;
01746       }
01747     error ("figure_manager: figure is not fltk");
01748     return -1;
01749   }
01750 
01751   static int hnd2idx (double h)
01752   {
01753     graphics_object fobj = gh_manager::get_object (h);
01754     if (fobj &&  fobj.isa ("figure"))
01755       {
01756         figure::properties& fp =
01757           dynamic_cast<figure::properties&> (fobj.get_properties ());
01758         return figprops2idx (fp);
01759       }
01760     error ("figure_manager: H (= %g) is not a figure", h);
01761     return -1;
01762   }
01763 
01764   static int hnd2idx (const graphics_handle& fh)
01765   {
01766     return hnd2idx (fh.value ());
01767   }
01768 };
01769 
01770 figure_manager *figure_manager::instance = 0;
01771 
01772 std::string figure_manager::fltk_idx_header="fltk index=";
01773 int figure_manager::curr_index = 1;
01774 
01775 static bool toolkit_loaded = false;
01776 
01777 static int
01778 __fltk_redraw__ (void)
01779 {
01780   if (toolkit_loaded)
01781     {
01782       // We scan all figures and add those which use FLTK.
01783       graphics_object obj = gh_manager::get_object (0);
01784       if (obj && obj.isa ("root"))
01785         {
01786           base_properties& props = obj.get_properties ();
01787           Matrix children = props.get_all_children ();
01788 
01789           for (octave_idx_type n = 0; n < children.numel (); n++)
01790             {
01791               graphics_object fobj = gh_manager::get_object (children (n));
01792               if (fobj && fobj.isa ("figure"))
01793                 {
01794                   figure::properties& fp =
01795                       dynamic_cast<figure::properties&> (fobj.get_properties ());
01796                   if (fp.get___graphics_toolkit__ ()
01797                       == FLTK_GRAPHICS_TOOLKIT_NAME)
01798                     figure_manager::new_window (fp);
01799                 }
01800             }
01801         }
01802 
01803       // it seems that we have to call Fl::check twice to get everything drawn
01804       Fl::check ();
01805       Fl::check ();
01806     }
01807 
01808   return 0;
01809 }
01810 
01811 class fltk_graphics_toolkit : public base_graphics_toolkit
01812 {
01813 public:
01814   fltk_graphics_toolkit (void)
01815     : base_graphics_toolkit (FLTK_GRAPHICS_TOOLKIT_NAME) { }
01816 
01817   ~fltk_graphics_toolkit (void) { }
01818 
01819   bool is_valid (void) const { return true; }
01820 
01821   bool initialize (const graphics_object& go)
01822     { return go.isa ("figure"); }
01823 
01824   void finalize (const graphics_object& go)
01825   {
01826     if (go.isa ("figure"))
01827       {
01828         octave_value ov = go.get (caseless_str ("__plot_stream__"));
01829 
01830         if (! ov.is_empty ())
01831           figure_manager::delete_window (ov.string_value ());
01832       }
01833   }
01834 
01835   void uimenu_set_fltk_label (graphics_object uimenu_obj)
01836   {
01837     if (uimenu_obj.valid_object ())
01838       {
01839         uimenu::properties& uimenup =
01840           dynamic_cast<uimenu::properties&> (uimenu_obj.get_properties ());
01841         std::string fltk_label = uimenup.get_label ();
01842         graphics_object go = gh_manager::get_object (uimenu_obj.get_parent ());
01843         if (go.isa ("uimenu"))
01844           fltk_label = dynamic_cast<const uimenu::properties&> (go.get_properties ()).get_fltk_label ()
01845             + "/"
01846             + fltk_label;
01847         else if (go.isa ("figure"))
01848           ;
01849         else
01850           error ("unexpected parent object\n");
01851 
01852         uimenup.set_fltk_label (fltk_label);
01853       }
01854   }
01855 
01856   void update (const graphics_object& go, int id)
01857   {
01858     if (go.isa ("figure"))
01859       {
01860         octave_value ov = go.get (caseless_str ("__plot_stream__"));
01861 
01862         if (! ov.is_empty ())
01863           {
01864             const figure::properties& fp =
01865               dynamic_cast<const figure::properties&> (go.get_properties ());
01866 
01867             switch (id)
01868               {
01869               case base_properties::ID_VISIBLE:
01870                 figure_manager::toggle_window_visibility
01871                   (ov.string_value (), fp.is_visible ());
01872                 break;
01873 
01874               case figure::properties::ID_MENUBAR:
01875                 figure_manager::toggle_menubar_visibility
01876                   (ov.string_value (), fp.menubar_is ("figure"));
01877                 break;
01878 
01879               case figure::properties::ID_CURRENTAXES:
01880                 figure_manager::update_canvas
01881                   (go.get_handle (), fp.get_currentaxes ());
01882                 break;
01883 
01884               case figure::properties::ID_NAME:
01885               case figure::properties::ID_NUMBERTITLE:
01886                 figure_manager::set_name (ov.string_value ());
01887                 break;
01888 
01889               case figure::properties::ID_INTEGERHANDLE:
01890                 {
01891                   std::string tmp = ov.string_value ();
01892                   graphics_handle gh = fp.get___myhandle__ ();
01893                   figure_manager::renumber_figure (tmp, gh.value ());
01894                   figure_manager::set_name (tmp);
01895                 }
01896                 break;
01897               }
01898           }
01899       }
01900     else if (go.isa ("uimenu"))
01901       {
01902         if (id == uimenu::properties::ID_LABEL)
01903           uimenu_set_fltk_label (go);
01904 
01905         graphics_object fig = go.get_ancestor("figure");
01906         figure_manager::uimenu_update(fig.get_handle (), go.get_handle (), id);
01907       }
01908   }
01909 
01910   void redraw_figure (const graphics_object& go) const
01911   {
01912     figure_manager::mark_modified (go.get_handle ());
01913 
01914     __fltk_redraw__ ();
01915   }
01916 
01917   void print_figure (const graphics_object& go,
01918                      const std::string& term,
01919                      const std::string& file_cmd, bool /*mono*/,
01920                      const std::string& /*debug_file*/) const
01921   {
01922     figure_manager::print (go.get_handle (), file_cmd, term);
01923     redraw_figure (go);
01924   }
01925 
01926   Matrix get_canvas_size (const graphics_handle& fh) const
01927   {
01928     return figure_manager::get_size (fh);
01929   }
01930 
01931   double get_screen_resolution (void) const
01932   {
01933     // FLTK doesn't give this info.
01934     return 72.0;
01935   }
01936 
01937   Matrix get_screen_size (void) const
01938   {
01939     Matrix sz (1, 2, 0.0);
01940     sz(0) = Fl::w ();
01941     sz(1) = Fl::h ();
01942     return sz;
01943   }
01944 
01945   void close (void)
01946   {
01947     if (toolkit_loaded)
01948       {
01949         munlock ("__init_fltk__");
01950 
01951         figure_manager::close_all ();
01952         gtk_manager::unload_toolkit (FLTK_GRAPHICS_TOOLKIT_NAME);
01953         toolkit_loaded = false;
01954 
01955         octave_value_list args;
01956         args(0) = "__fltk_redraw__";
01957         feval ("remove_input_event_hook", args, 0);
01958 
01959         // FIXME ???
01960         Fl::wait (fltk_maxtime);
01961       }
01962   }
01963 };
01964 
01965 // Initialize the fltk graphics toolkit.
01966 
01967 DEFUN_DLD (__init_fltk__, , , "")
01968 {
01969   if (! toolkit_loaded)
01970     {
01971       mlock ();
01972 
01973       graphics_toolkit tk (new fltk_graphics_toolkit ());
01974       gtk_manager::load_toolkit (tk);
01975       toolkit_loaded = true;
01976 
01977       octave_value_list args;
01978       args(0) = "__fltk_redraw__";
01979       feval ("add_input_event_hook", args, 0);
01980     }
01981 
01982   octave_value retval;
01983   return retval;
01984 }
01985 
01986 DEFUN_DLD (__fltk_redraw__, , , "")
01987 {
01988   __fltk_redraw__ ();
01989 
01990   return octave_value ();
01991 }
01992 
01993 DEFUN_DLD (__fltk_maxtime__, args, ,"")
01994 {
01995   octave_value retval = fltk_maxtime;
01996 
01997   if (args.length () == 1)
01998     {
01999       if (args(0).is_real_scalar ())
02000         fltk_maxtime = args(0).double_value ();
02001       else
02002         error ("argument must be a real scalar");
02003     }
02004 
02005   return retval;
02006 }
02007 
02008 #endif
02009 
02010 // FIXME -- This function should be abstracted and made potentially
02011 // available to all graphics toolkits.  This suggests putting it in
02012 // graphics.cc as is done for drawnow() and having the master
02013 // mouse_wheel_zoom function call fltk_mouse_wheel_zoom.  The same
02014 // should be done for gui_mode and fltk_gui_mode.  For now (2011.01.30),
02015 // just changing function names and docstrings.
02016 
02017 DEFUN_DLD (mouse_wheel_zoom, args, ,
02018   "-*- texinfo -*-\n\
02019 @deftypefn  {Built-in Function} {@var{speed} =} mouse_wheel_zoom ()\n\
02020 @deftypefnx {Built-in Function} {} mouse_wheel_zoom (@var{speed})\n\
02021 Query or set the mouse wheel zoom factor.\n\
02022 \n\
02023 This function is currently implemented only for the FLTK graphics toolkit.\n\
02024 @seealso{gui_mode}\n\
02025 @end deftypefn")
02026 {
02027 #if defined (HAVE_FLTK)
02028   octave_value retval = wheel_zoom_speed;
02029 
02030   if (args.length () == 1)
02031     {
02032       if (args(0).is_real_scalar ())
02033         wheel_zoom_speed = args(0).double_value ();
02034       else
02035         error ("mouse_wheel_zoom: SPEED must be a real scalar");
02036     }
02037 
02038   return retval;
02039 #else 
02040   error ("mouse_wheel_zoom: not available without OpenGL and FLTK libraries");
02041   return octave_value ();
02042 #endif
02043 }
02044 
02045 DEFUN_DLD (gui_mode, args, ,
02046   "-*- texinfo -*-\n\
02047 @deftypefn  {Built-in Function} {@var{mode} =} gui_mode ()\n\
02048 @deftypefnx {Built-in Function} {} gui_mode (@var{mode})\n\
02049 Query or set the GUI mode for the current graphics toolkit.\n\
02050 The @var{mode} argument can be one of the following strings:\n\
02051 @table @asis\n\
02052 @item '2d'\n\
02053 Allows panning and zooming of current axes.\n\
02054 \n\
02055 @item '3d'\n\
02056 Allows rotating and zooming of current axes.\n\
02057 \n\
02058 @item 'none'\n\
02059 Mouse inputs have no effect.\n\
02060 @end table\n\
02061 \n\
02062 This function is currently implemented only for the FLTK graphics toolkit.\n\
02063 @seealso{mouse_wheel_zoom}\n\
02064 @end deftypefn")
02065 {
02066 #if defined (HAVE_FLTK)
02067   caseless_str mode_str;
02068 
02069   if (gui_mode == pan_zoom)
02070     mode_str = "2d";
02071   else if (gui_mode == rotate_zoom)
02072     mode_str = "3d";
02073   else
02074     mode_str = "none";
02075 
02076   bool failed = false;
02077 
02078   if (args.length () == 1)
02079     {
02080       if (args(0).is_string ())
02081         {
02082           mode_str = args(0).string_value ();
02083 
02084           if (mode_str.compare ("2d"))
02085             gui_mode = pan_zoom;
02086           else if (mode_str.compare ("3d"))
02087             gui_mode = rotate_zoom;
02088           else if (mode_str.compare ("none"))
02089             gui_mode = none;
02090           else
02091             failed = true;
02092         }
02093       else
02094         failed = true;
02095     }
02096 
02097   if (failed)
02098     error ("MODE must be one of the strings: \"2D\", \"3D\", or \"none\"");
02099 
02100   return octave_value (mode_str);
02101 #else
02102   error ("mouse_wheel_zoom: not available without OpenGL and FLTK libraries");
02103   return octave_value ();
02104 #endif
02105 }
02106 
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines