OSDN Git Service

Rotation by slider shows the rotation angle (and snap to the multiples of 15 deg)
[molby/Molby.git] / wxSources / MoleculeView.cpp
1 /*
2  *  MoleculeView.cpp
3  *  Molby
4  *
5  *  Created by Toshi Nagata on 08/10/24.
6  *  Copyright 2008 Toshi Nagata. All rights reserved.
7  *
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation version 2 of the License.
11  
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  GNU General Public License for more details.
16  */
17
18 // For compilers that support precompilation, includes "wx/wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WX_PRECOMP
26 #include "wx/wx.h"
27 #endif
28
29 #if !wxUSE_DOC_VIEW_ARCHITECTURE
30 #error "You should have DocView architecture enabled in your wxWidgets installation."
31 #endif
32
33 #include "MoleculeView.h"
34
35 #include "MyApp.h"
36 #include "MyDocument.h"
37 #include "MyGLCanvas.h"
38 #include "MyCommand.h"
39 #include "MySlider.h"
40 #include "MyListCtrl.h"
41 #include "../MolLib/Missing.h"
42 #include "../MolLib/Ruby_bind/Molby_extern.h"
43 #include "MyMBConv.h"
44 #include "MyProgressIndicator.h"
45
46 #include "wx/tglbtn.h"
47 #include "wx/listctrl.h"
48 #include "wx/splitter.h"
49 #include "wx/choice.h"
50 #include "wx/font.h"
51
52 #if defined(__WXMSW__)
53 #include "OpenGL_extensions.h"
54 #endif
55
56 //#include "../MolLib/Ruby_bind/Molby_extern.h"
57
58 enum {
59         myID_RotButton = 500,
60         myID_TransButton,
61         myID_ScaleButton,
62         myID_SelectButton,
63         myID_BondButton,
64         myID_EraseButton,
65         myID_RotateBondSlider,
66         myID_RotateXSlider,
67         myID_RotateYSlider,
68         myID_FrameControlPanel,
69         myID_FrameSlider,
70         myID_JumpToStartButton,
71         myID_PlayBackwardButton,
72         myID_FrameText,
73         myID_PlayForwardButton,
74         myID_JumpToEndButton,
75         myID_Table,
76         myID_TableMenu,
77         myID_StopProgressButton
78 };
79
80 IMPLEMENT_DYNAMIC_CLASS(MoleculeView, wxView)
81
82 BEGIN_EVENT_TABLE(MoleculeView, wxView)
83         EVT_TOGGLEBUTTON(myID_RotButton, MoleculeView::OnButtonPressed)
84         EVT_TOGGLEBUTTON(myID_TransButton, MoleculeView::OnButtonPressed)
85         EVT_TOGGLEBUTTON(myID_ScaleButton, MoleculeView::OnButtonPressed)
86         EVT_TOGGLEBUTTON(myID_SelectButton, MoleculeView::OnButtonPressed)
87         EVT_TOGGLEBUTTON(myID_BondButton, MoleculeView::OnButtonPressed)
88         EVT_TOGGLEBUTTON(myID_EraseButton, MoleculeView::OnButtonPressed)
89         EVT_BUTTON(myID_StopProgressButton, MoleculeView::OnStopProgressPressed)
90         EVT_COMMAND(wxID_ANY, MySliderEvent, MoleculeView::OnSliderAction)
91         EVT_COMMAND_SCROLL(myID_FrameSlider, MoleculeView::OnFrameSliderAction)
92         EVT_TEXT_ENTER(myID_FrameText, MoleculeView::OnFrameTextAction)
93         EVT_CHOICE(myID_TableMenu, MoleculeView::OnSelectTable)
94         EVT_ACTIVATE(MoleculeView::OnActivate)
95 END_EVENT_TABLE()
96 #define ConnectMouseDownEvents(src, func, target) \
97         (src->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(func), NULL, target), \
98         src->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(func), NULL, target))
99                                  
100 WX_DEFINE_ARRAY_PTR(MoleculeView *, ArrayOfMoleculeViews);
101
102 ArrayOfMoleculeViews sActiveViews;
103
104 MoleculeView::~MoleculeView()
105 {
106 }
107
108 bool
109 MoleculeView::OnCreate(wxDocument *doc, long WXUNUSED(flags) )
110 {
111         int i;
112
113         const wxFont *ctrlFont;
114 #if __WXOSX_COCOA__
115         ctrlFont = wxSMALL_FONT;
116 #else
117         ctrlFont = NULL;
118 #endif
119         
120         // Make a document frame
121         frame = new wxDocChildFrame(doc, this, GetMainFrame(), wxID_ANY, _T("New Molby Document"),
122                                                    wxDefaultPosition, wxDefaultSize,
123                                                    wxDEFAULT_FRAME_STYLE |
124                                                    wxNO_FULL_REPAINT_ON_RESIZE);
125     frame->SetPosition(FromFrameDIP(frame, wxPoint(10, 24)));
126     frame->SetClientSize(FromFrameDIP(frame, wxSize(680, 400)));
127         canvas = NULL;
128         mview = NULL;
129         listmenu = NULL;
130         listctrl = NULL;
131         file_history_menu = NULL;
132         edit_menu = NULL;
133         memset(tbuttons, 0, sizeof(tbuttons));
134         infotext = NULL;
135         frameControlPanel = NULL;
136         frameSlider = NULL;
137         frameText = NULL;
138         isRebuildingTable = false;
139
140         Molecule *mol = ((MyDocument *)doc)->GetMolecule();
141         if (mol != NULL) {
142                 mview = mol->mview;
143                 MainView_setViewObject(mview, this);
144         }
145
146         wxMenuBar *menu_bar = wxGetApp().CreateMenuBar(1, &file_history_menu, &edit_menu);
147         
148         // Associate the menu bar with the frame
149         frame->SetMenuBar(menu_bar);
150
151         // Associate the edit menu with the command processor
152         doc->GetCommandProcessor()->SetEditMenu(edit_menu);
153         
154         // Create the window content
155         
156         //  A splitter window embraces a grid (left) and main screen (right)
157         wxSplitterWindow *splitter = new wxSplitterWindow(frame, -1, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE);
158         splitter->SetMinimumPaneSize(1);
159         
160         //  Create the left half
161         //  A panel containing a popup menu and a list window
162         wxPanel *panel0 = new wxPanel(splitter);
163         {
164                 char buf[32];
165                 wxBoxSizer *sizer0;
166                 sizer0 = new wxBoxSizer(wxVERTICAL);
167                 wxArrayString choiceItems;
168                 for (i = 0; ; i++) {
169                         MainView_tableTitleForIndex(mview, i, buf, sizeof buf);
170                         if (buf[0] == 0)
171                                 break;
172                         wxString itemTitle(buf, WX_DEFAULT_CONV);
173                         choiceItems.Add(itemTitle);
174                 }
175                 
176                 listmenu = new wxChoice(panel0, myID_TableMenu, wxDefaultPosition, wxDefaultSize, choiceItems);
177                 sizer0->Add(listmenu, 0, wxALL, 0);
178                 
179                 listctrl = new MyListCtrl();
180                 listctrl->Create(panel0, myID_Table, wxDefaultPosition, wxDefaultSize);
181                 sizer0->Add(listctrl, 1, wxALL | wxEXPAND, 0);
182                 panel0->SetSizer(sizer0);               
183         }
184                 
185         //  Create the right half
186         //  A panel containing MyGLCanvas, buttons, sliders, etc.
187         wxPanel *panel1 = new wxPanel(splitter);
188         
189         {       //  Vertical sizer containing [sizer2, sizer3, sizer4]
190                 wxBoxSizer *sizer1;
191                 sizer1 = new wxBoxSizer(wxVERTICAL);
192                 
193                 {       //  Horizontal sizer containing [button0, ..., button5, infotext, progress_indicator]
194                         wxBoxSizer *sizer2;
195                         sizer2 = new wxBoxSizer(wxHORIZONTAL);
196                         
197                         {       // Button0..5 (Rot/Trans/Scale/Select/Bond/Erase)
198                                 wxString labels[] = {
199                                         wxT("Rot"), wxT("Trans"), wxT("Scale"), wxT("Select"), wxT("Bond"), wxT("Erase")
200                                 };
201                                 wxWindowID ids[] = {
202                                         myID_RotButton, myID_TransButton, myID_ScaleButton, 
203                                         myID_SelectButton, myID_BondButton, myID_EraseButton
204                                 };
205                                 for (i = 0; i < 6; i++) {
206                                         tbuttons[i] = new MyToggleButton(panel1, ids[i], labels[i], wxDefaultPosition, FromFrameDIP(frame, wxSize(40, 32)), wxTOGGLEBUTTON_STYLE);
207                                         if (ctrlFont)
208                                                 tbuttons[i]->SetFont(*ctrlFont);
209                                         sizer2->Add(tbuttons[i], 0, wxALL | wxEXPAND, FromFrameDIP(frame, 3));
210                                 }
211                                 tbuttons[0]->SetValue(true);
212                         }
213                         {       // Information text
214                                 infotext = new wxStaticText(panel1, -1, wxT(""), wxDefaultPosition, FromFrameDIP(frame, wxSize(80, 32)), wxST_NO_AUTORESIZE | wxBORDER_SUNKEN);
215                                 infotext->SetMinSize(FromFrameDIP(frame, wxSize(80, 32)));
216 #if defined(__WXMSW__)
217                                 infotext->SetFont(*wxSMALL_FONT);
218 #else
219                                 if (ctrlFont)
220                                         infotext->SetFont(*ctrlFont);
221 #endif
222                                 sizer2->Add(infotext, 1, wxALL | wxEXPAND, 3);   // Can expand horizontally
223                         }
224                         {       // Custom progress indicator
225                                 progress = new MyProgressIndicator(panel1, myID_StopProgressButton, wxDefaultPosition, FromFrameDIP(frame, wxSize(12, 24)));
226                                 sizer2->Add(progress, 0, wxALL | wxEXPAND, 3);
227                         }
228                         sizer1->Add(sizer2, 0, wxALL | wxEXPAND, 0);
229                 }
230                 
231                 {       // Horizontal sizer containing [sizer31, sizer32, sizer33]
232                         wxBoxSizer *sizer3 = new wxBoxSizer(wxHORIZONTAL);
233                         
234                         {       // Vertical sizer containing [button, mySlider]
235                                 wxBoxSizer *sizer31 = new wxBoxSizer(wxVERTICAL);
236                                 {       // "Rotate bond" button and mySlider
237                                         #include "../bitmaps/rotate_bond.xpm"
238                                         wxBitmap bmp1(rotate_bond_xpm);
239                                         wxBitmapButton *button1 = new wxBitmapButton(panel1, -1, bmp1, wxDefaultPosition, FromFrameDIP(frame, wxSize(21, 21)), wxTOGGLEBUTTON_STYLE);
240                                         sizer31->Add(button1, 0, 0, 0);
241                                         button1->Disable();
242                                         MySlider *slider1 = new MySlider(panel1, myID_RotateBondSlider, wxVERTICAL, wxDefaultPosition, FromFrameDIP(frame, wxSize(21, 21)));
243                                         sizer31->Add(slider1, 1, wxEXPAND);
244                                 }
245                                 sizer3->Add(sizer31, 0, wxALL | wxEXPAND, 0);
246                         }
247                         
248                         {       // Vertical sizer containing [Canvas, [button, mySlider]]
249                                 wxBoxSizer *sizer32 = new wxBoxSizer(wxVERTICAL);
250                                 {
251                                         canvas = new MyGLCanvas(this, panel1, wxDefaultPosition, FromFrameDIP(frame, wxSize(100, 100)));
252                                         sizer32->Add(canvas, 1, wxALL | wxEXPAND, 0);
253                                         
254                                         //  Let the MyGLCanvas pass the keyboard event to this
255                                         canvas->Connect(-1, wxEVT_CHAR, wxKeyEventHandler(MoleculeView::OnChar), NULL, this);
256                 }
257                                 {
258                                         wxBoxSizer *sizer321 = new wxBoxSizer(wxHORIZONTAL);
259                                         {
260                                                 #include "../bitmaps/rotate_y.xpm"
261                                                 wxBitmap bmp2(rotate_y_xpm);
262                                                 wxBitmapButton *button2 = new wxBitmapButton(panel1, -1, bmp2, wxDefaultPosition, FromFrameDIP(frame, wxSize(21, 21)), wxTOGGLEBUTTON_STYLE);
263                                                 sizer321->Add(button2, 0, 0, 0);
264                                                 button2->Disable();
265                                                 MySlider *slider2 = new MySlider(panel1, myID_RotateYSlider, wxHORIZONTAL, wxDefaultPosition, FromFrameDIP(frame, wxSize(21, 21)));
266                                                 sizer321->Add(slider2, 1, wxEXPAND);
267                                         }
268                                         sizer32->Add(sizer321, 0, wxEXPAND);
269                                 }
270                                 sizer3->Add(sizer32, 1, wxEXPAND);
271                         }
272
273                         {       // Vertical sizer containing [button, mySlider]
274                                 wxBoxSizer *sizer33 = new wxBoxSizer(wxVERTICAL);
275                                 {       // "Rotate bond" button and mySlider
276                                         #include "../bitmaps/rotate_x.xpm"
277                                         wxBitmap bmp3(rotate_x_xpm);
278                                         wxBitmapButton *button3 = new wxBitmapButton(panel1, -1, bmp3, wxDefaultPosition, FromFrameDIP(frame, wxSize(21, 21)), wxTOGGLEBUTTON_STYLE);
279                                         button3->Disable();
280                                         sizer33->Add(button3, 0, 0, 0);
281                                         
282                                         MySlider *slider3 = new MySlider(panel1, myID_RotateXSlider, wxVERTICAL, wxDefaultPosition, FromFrameDIP(frame, wxSize(21, 21)));
283                                         sizer33->Add(slider3, 1, wxEXPAND);
284                                 }
285                                 sizer3->Add(sizer33, 0, wxALL | wxEXPAND, 0);
286                         }
287                         
288                         sizer1->Add(sizer3, 1, wxALL | wxEXPAND, 0);
289                 }
290                 
291                 {       //  Horizontal sizer containing frame controls
292                         
293                         const int height = 18;
294                         frameControlPanel = new wxPanel(panel1, myID_FrameControlPanel, wxDefaultPosition, FromFrameDIP(frame, wxSize(200, height)));
295                         wxBoxSizer *sizer4 = new wxBoxSizer(wxHORIZONTAL);
296                         {
297                                 frameSlider = new wxSlider(frameControlPanel, myID_FrameSlider, 0, 0, 1, wxDefaultPosition, FromFrameDIP(frame, wxSize(40, height - 2)));
298                                 frameSlider->Enable(false);
299                                 sizer4->Add(frameSlider, 1, wxALL | wxEXPAND, 1);
300                         
301                                 #include "../bitmaps/jump_to_start.xpm"
302                                 wxBitmap bmp41(jump_to_start_xpm);
303                                 wxBitmapButton *button41 = new wxBitmapButton(frameControlPanel, myID_JumpToStartButton, bmp41, wxDefaultPosition, FromFrameDIP(frame, wxSize(16, height)), wxTOGGLEBUTTON_STYLE);
304                                 sizer4->Add(button41, 0, wxEXPAND);
305                                 ConnectMouseDownEvents(button41, MoleculeView::OnFrameButtonAction, this);
306
307                                 #include "../bitmaps/play_backward.xpm"
308                                 wxBitmap bmp42(play_backward_xpm);
309                                 wxBitmapButton *button42 = new wxBitmapButton(frameControlPanel, myID_PlayBackwardButton, bmp42, wxDefaultPosition, FromFrameDIP(frame, wxSize(16, height)), wxTOGGLEBUTTON_STYLE);
310                                 sizer4->Add(button42, 0, wxEXPAND);
311                                 ConnectMouseDownEvents(button42, MoleculeView::OnFrameButtonAction, this);
312                                 
313                                 {
314                                         frameText = new wxTextCtrl(frameControlPanel, myID_FrameText, wxT(""), wxDefaultPosition, FromFrameDIP(frame, wxSize(40, height)));
315                                         if (ctrlFont) {
316                                                 wxTextAttr attr(*wxBLACK, wxNullColour, *ctrlFont);
317                                                 frameText->SetDefaultStyle(attr);
318                                                 frameText->SetFont(*ctrlFont);
319                                         }
320                                         sizer4->Add(frameText, 0, wxEXPAND);
321                                 }
322                         
323                                 #include "../bitmaps/play_forward.xpm"
324                                 wxBitmap bmp43(play_forward_xpm);
325                                 wxBitmapButton *button43 = new wxBitmapButton(frameControlPanel, myID_PlayForwardButton, bmp43, wxDefaultPosition, FromFrameDIP(frame, wxSize(16, height)), wxTOGGLEBUTTON_STYLE);
326                                 sizer4->Add(button43, 0, wxEXPAND);
327                                 ConnectMouseDownEvents(button43, MoleculeView::OnFrameButtonAction, this);
328
329                                 #include "../bitmaps/jump_to_end.xpm"
330                                 wxBitmap bmp44(jump_to_end_xpm);
331                                 wxBitmapButton *button44 = new wxBitmapButton(frameControlPanel, myID_JumpToEndButton, bmp44, wxDefaultPosition, FromFrameDIP(frame, wxSize(16, height)), wxTOGGLEBUTTON_STYLE);
332                                 sizer4->Add(button44, 0, wxEXPAND);
333                                 ConnectMouseDownEvents(button44, MoleculeView::OnFrameButtonAction, this);
334                                 
335                                 wxPanel *spacer = new wxPanel(frameControlPanel, -1, wxDefaultPosition, FromFrameDIP(frame, wxSize(21, height)));
336                                 sizer4->Add(spacer, 0, wxEXPAND);
337                         }
338                         frameControlPanel->SetSizer(sizer4);
339                         sizer1->Add(frameControlPanel, 0, wxALL | wxEXPAND, 0);
340                 //      controls->Disable();
341                 }
342
343                 panel1->SetSizer(sizer1);
344         }
345
346         splitter->SplitVertically(panel0, panel1);
347
348         wxBoxSizer *mainsizer = new wxBoxSizer(wxHORIZONTAL);
349         mainsizer->Add(splitter, 1, wxEXPAND);
350         frame->SetSizer(mainsizer);
351
352         mainsizer->Layout();
353         splitter->SetSashPosition(FromFrameDIP(frame, 240), true);
354
355         //  Initialize table view
356         MainView_createColumnsForTableAtIndex(mview, 0);
357
358         //  Select table popup
359         listmenu->SetSelection(0);
360         
361 #if defined(__X__) || defined(__WXMAC__)
362     // X seems to require a forced resize
363     int x, y;
364     frame->GetSize(&x, &y);
365     frame->SetSize(wxDefaultCoord, wxDefaultCoord, x, y);
366 #endif
367     frame->Show(true);
368     Activate(true);
369
370         //  Initial keyboard focus is on the GL canvas (to accept 'S' for scale, etc.)
371         canvas->SetFocus();
372         
373         //  Connect the notification handler
374         doc->Connect(MyDocumentEvent_documentModified, MyDocumentEvent, wxCommandEventHandler(MoleculeView::OnDocumentModified), NULL, this);
375
376         wxGetApp().Connect(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, wxCommandEventHandler(MoleculeView::OnScriptMenuModified), NULL, this);
377
378         //  Intercept the double-click handler of MyListCtrl
379         listctrl->GetScrolledWindow()->Bind(wxEVT_LEFT_DCLICK, &MoleculeView::OnLeftDClickInListCtrl, this);
380
381         //  Set data source for the list control
382         listctrl->SetDataSource(this);
383                                 
384 #if defined(__WXMSW__)
385         //  Initialize OpenGL extension
386         {
387                 static int openGLExtension_inited = 0;
388                 if (openGLExtension_inited == 0) {
389                         if (InitializeOpenGLExtensions() != 0) {
390                                 MyAppCallback_errorMessageBox("Fatal internal error: cannot initialize OpenGL extensions");
391                                 openGLExtension_inited = -1;
392                         } else openGLExtension_inited = 1;
393                 }
394         }
395         
396 #endif
397         
398     return true;
399 }
400
401 void
402 MoleculeView::OnDraw(wxDC *dc)
403 {
404         if (mview != NULL) {
405                 if (mview->isPrinting) {
406                         float scale = 4.0;
407                         wxImage *img = CaptureGLCanvas(scale);
408                         if (img != NULL) {
409                                 wxBitmap bitmap(*img);
410                                 double sx, sy;
411                                 dc->GetUserScale(&sx, &sy);
412                                 dc->SetUserScale(sx / scale, sy / scale);
413                                 dc->DrawBitmap(bitmap, 0, 0);
414                                 delete img;
415                         }
416                         mview->isPrinting = 0;
417                 } else {
418                         if (mview->mol != NULL) {
419                                 MoleculeLock(mview->mol);
420                                 MainView_drawModel(mview);
421                                 MoleculeUnlock(mview->mol);
422                         }
423                 }
424         }
425 }
426
427 wxImage *
428 MoleculeView::CaptureGLCanvas(float scale, int bg_color, int width, int height)
429 {
430         if (canvas && mview->mol != NULL) {
431                 int x, y, cwidth, cheight;
432                 float bgcol[4], rx, ry;
433                 
434                 canvas->SetCurrent();
435                 canvas->GetClientSize(&cwidth, &cheight);
436                 if (width <= 0)
437                         width = cwidth;
438                 if (height <= 0)
439                         height = cheight;
440                 rx = (float)width / cwidth;
441                 ry = (float)height / cheight;
442                 if (rx > ry)
443                         rx = ry;
444                 width *= scale;
445                 height *= scale;
446
447                 //  Create OpenGL offscreen buffer
448                 GLuint frame_buf, render_buf, depth_buf;
449                 frame_buf = render_buf = depth_buf = 0;
450                 glGenFramebuffersEXT(1, &frame_buf);
451                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frame_buf);
452                 
453                 glGenRenderbuffersEXT(1, &render_buf);
454                 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buf);
455                 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA, width, height);
456
457                 glGenRenderbuffersEXT(1, &depth_buf);
458                 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_buf);
459                 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);
460                 
461                 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, render_buf);
462                 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_buf);
463
464                 MainView_initializeOpenGL();
465
466                 mview->offline_scale = 1.0;  /*  Scale is handled in offline_width and offline_height  */
467                 mview->offline_width = width;
468                 mview->offline_height = height;
469                 
470                 for (x = 0; x < 4; x++) {
471                         bgcol[x] = mview->background_color[x];
472                 }
473                 if (bg_color == 0) {
474                         mview->background_color[0] = 0;
475                         mview->background_color[1] = 0;
476                         mview->background_color[2] = 0;
477                         mview->background_color[3] = 0;
478                 } else if (bg_color == 1) {
479                         mview->background_color[0] = 0;
480                         mview->background_color[1] = 0;
481                         mview->background_color[2] = 0;
482                         mview->background_color[3] = 1;
483                 } else if (bg_color == 2) {
484                         mview->background_color[0] = 1;
485                         mview->background_color[1] = 1;
486                         mview->background_color[2] = 1;
487                         mview->background_color[3] = 1;
488                 }
489                 MoleculeLock(mview->mol);
490                 MainView_drawModel(mview);
491                 MoleculeUnlock(mview->mol);
492                 for (x = 0; x < 4; x++) {
493                         mview->background_color[x] = bgcol[x];
494                 }
495                 mview->offline_scale = 0.0;
496                 mview->offline_width = 0;
497                 mview->offline_height = 0;
498
499                 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
500                 unsigned char *glBitmapData = (unsigned char *)malloc(4 * width * height);
501                 unsigned char *glRGBData = (unsigned char *)malloc(3 * width * height);
502                 unsigned char *glAlphaData = (unsigned char *)malloc(width * height);
503                 glReadPixels((GLint)0, (GLint)0, (GLint)width, (GLint)height, GL_RGBA, GL_UNSIGNED_BYTE, glBitmapData);
504                 for (y = 0; y < height; y++) {
505                         unsigned char *p1 = glBitmapData + 4 * y * width;
506                         unsigned char *p2 = glRGBData + 3 * (height - 1 - y) * width;
507                         unsigned char *p3 = glAlphaData + (height - 1 - y) * width;
508                         for (x = 0; x < width; x++) {
509                                 //  Copy RGB data and Alpha data separately, with flipping vertically
510                                 *p2++ = *p1++;
511                                 *p2++ = *p1++;
512                                 *p2++ = *p1++;
513                                 *p3++ = *p1++;
514                         }
515                 }
516                 wxImage *img = new wxImage(width, height, glRGBData, glAlphaData);
517                 free(glBitmapData);  // glRGBData and glAlphaData are deallocated within wxImage constructor
518                 
519                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
520                 glDeleteFramebuffersEXT(1, &frame_buf);
521                 glDeleteRenderbuffersEXT(1, &render_buf);
522                 
523                 canvas->Refresh();
524         
525                 return img;
526         }
527         return NULL;
528 }
529
530 int
531 MoleculeView::DoExportGraphic(wxString& fname, float scale, int bg_color, int width, int height)
532 {
533         wxImage *img = CaptureGLCanvas(scale, bg_color, width, height);
534         if (img == NULL)
535                 return -1;
536         wxString ext = fname.AfterLast('.');
537         wxBitmapType type = wxBITMAP_TYPE_PNG;
538         if (ext.CmpNoCase(_T("tif")) == 0)
539                 type = wxBITMAP_TYPE_TIF;
540         img->SaveFile(fname, type);
541         delete img;
542         return 0;
543 }
544
545 void
546 MoleculeView::OnUpdate(wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint))
547 {
548         if (canvas)
549                 canvas->Refresh();
550
551 /*  Maybe necessary in some platforms (not in MacOSX and MSW)  */
552 #if 0
553   if (canvas) {
554       wxClientDC dc(canvas);
555       dc.Clear();
556       OnDraw(&dc);
557   }
558 #endif
559 }
560
561 bool
562 MoleculeView::OnClose(bool deleteWindow)
563 {
564 //#if !defined(__WXMAC__) && !defined(__WXOSX__)
565         //  On wxOSX, this causes invocation of MyDocument::Close() twice, which
566         //  apprently is not very good. However, on wxMSW this is not the case.
567         //  So we need to keep this code for wxMSW but not for wxOSX.
568         if (!GetDocument()->Close())
569                 return false;
570 //#endif
571
572         //  Dispose relationship between this and Molecule (MainView)
573         MainView_setViewObject(mview, NULL);
574         mview = NULL;
575
576         //  Remove this from the active view list
577         sActiveViews.Remove(this);
578
579         //  Dispose Connection between DocManager and file history menu
580         wxGetApp().DocManager()->FileHistoryRemoveMenu(file_history_menu);
581
582         wxGetApp().Disconnect(MyDocumentEvent_scriptMenuModified, MyDocumentEvent, wxCommandEventHandler(MoleculeView::OnScriptMenuModified), NULL, this);
583
584         if (deleteWindow) {
585                 frame->Destroy();
586         }
587         
588         //  Check if all windows are gone
589         wxGetApp().CheckIfAllWindowsAreGone(frame);
590
591         return true;
592 }
593
594 void
595 MoleculeView::Activate(bool activate)
596 {
597         if (activate) {
598                 int i, n = sActiveViews.GetCount();
599                 for (i = 0; i < n; i++) {
600                         if (sActiveViews.Item(i) == this)
601                                 break;
602                 }
603                 if (i < n) {
604                         /*  Remove this if existing  */
605                         sActiveViews.RemoveAt(i);
606                 }
607                 /*  Insert self as the first item  */
608                 sActiveViews.Insert(this, 0);
609         }
610         wxView::Activate(activate);
611     frame->Refresh();
612 }
613
614 wxPrintout *
615 MoleculeView::OnCreatePrintout()
616 {
617         if (mview != NULL)
618                 mview->isPrinting = 1;
619         return wxView::OnCreatePrintout();
620 }
621
622 void
623 MoleculeView::UpdateFrameControlValues()
624 {
625         if (mview == NULL || mview->mol == NULL)
626                 return;
627         wxString str;
628         int cframe;
629         cframe = mview->mol->cframe;
630         str.Printf(_T("%d"), cframe);
631 //      frameText->SetValue(str);
632         frameText->Clear();
633         frameText->AppendText(str);
634         frameSlider->SetValue(cframe);
635 }
636
637 void
638 MoleculeView::UpdateFrameControls()
639 {
640         int nframes;
641         bool enabled = false;
642         if (mview != NULL && mview->mol != NULL) {
643                 MoleculeLock(mview->mol);
644                 nframes = MoleculeGetNumberOfFrames(mview->mol);
645                 MoleculeUnlock(mview->mol);
646                 if (nframes > 1)
647                         enabled = true;
648         }
649         
650         frameControlPanel->Enable(enabled);
651         if (enabled) {
652                 frameSlider->Enable(true);
653                 frameSlider->SetRange(0, nframes - 1);
654                 UpdateFrameControlValues();
655         } else {
656                 frameSlider->Enable(false);
657                 frameSlider->SetRange(0, 1);
658                 frameText->SetValue(_T(""));
659         }
660         frameControlPanel->Update();
661 }
662
663 void
664 MoleculeView::InvalidateProgressIndicator()
665 {
666         progress->Refresh();
667 }
668
669 void
670 MoleculeView::ProceedProgressIndicator()
671 {
672         progress->ProceedIndicatorState();
673 }
674
675 void
676 MoleculeView::SelectButtonForMode(int mode)
677 {
678         int i;
679         if (mode >= 1 && mode <= 6) {
680                 for (i = 0; i < 6; i++) {
681                         tbuttons[i]->SetValue((i == mode - 1));
682                 }
683         }
684         MainViewCallback_setKeyboardFocus(mview);
685 }
686
687 void
688 MoleculeView::OnButtonPressed(wxCommandEvent& event)
689 {
690         int eventId = event.GetId();
691         int mode = eventId - myID_RotButton + kTrackballRotateMode;
692         MainView_setMode(mview, mode);
693         SelectButtonForMode(mode);
694 }
695
696 void
697 MoleculeView::OnSliderAction(wxCommandEvent& event)
698 {
699         int eventId = event.GetId();
700         int mode = eventId - myID_RotateBondSlider + 1;
701     char buf[256];
702         MySlider *sender = (MySlider *)event.GetEventObject();
703         float angle = sender->GetFloatValue();
704     float angledeg = angle * 360.0;
705     float fixangledeg = floor(angledeg / 15.0 + 0.5) * 15.0;  //  Nearest multiple of 15 deg
706     if (angledeg > fixangledeg - 0.9 && angledeg < fixangledeg + 0.9) {
707         angledeg = fixangledeg;  //  Snap to the nearest multiple of 15 deg
708         angle = angledeg / 360.0;
709     }
710         int mouseStatus = sender->GetMouseStatus();
711     
712         MoleculeLock(mview->mol);
713         MainView_rotateBySlider(mview, angle * 3.1415927 * 2, mode, mouseStatus, MainViewCallback_modifierFlags(NULL));
714     snprintf(buf, sizeof buf, "%.1f°", angledeg);
715     MainViewCallback_drawInfoText(mview, buf);
716     MainViewCallback_updateCanvas(mview);
717         MoleculeUnlock(mview->mol);
718 }
719
720 void
721 MoleculeView::OnFrameButtonAction(wxMouseEvent &event)
722 {
723         int ival, nframes, bid;
724         if (mview == NULL || mview->mol == NULL)
725                 goto skip;
726         nframes = MoleculeGetNumberOfFrames(mview->mol);
727         if (nframes == 0)
728                 goto skip;
729         bid = event.GetId();
730         if (bid == myID_JumpToStartButton) {
731                 ival = 0;
732         } else if (bid == myID_JumpToEndButton) {
733                 ival = nframes - 1;
734         } else if (bid == myID_PlayForwardButton) {
735                 ival = (mview->mol->cframe + 1) % nframes;
736         } else if (bid == myID_PlayBackwardButton) {
737                 ival = (mview->mol->cframe + nframes - 1) % nframes;
738         }
739         //  TODO: implement continuous move
740         if (ival >= 0 && ival < nframes) {
741                 MoleculeLock(mview->mol);
742                 MoleculeSelectFrame(mview->mol, ival, 1);
743                 MoleculeUnlock(mview->mol);
744                 MainViewCallback_display(mview);
745                 UpdateFrameControlValues();
746         }
747         
748 skip:
749         event.Skip();
750 }
751
752 void
753 MoleculeView::OnFrameSliderAction(wxScrollEvent &event)
754 {
755         int ival, nframes;
756         if (mview == NULL || mview->mol == NULL)
757                 return;
758         MoleculeLock(mview->mol);
759         nframes = MoleculeGetNumberOfFrames(mview->mol);
760         if (nframes != 0) {
761                 ival = frameSlider->GetValue();
762                 if (ival >= 0 && ival < nframes) {
763                         MoleculeSelectFrame(mview->mol, ival, 1);
764                         MoleculeUnlock(mview->mol);
765                         MainViewCallback_display(mview);
766                         UpdateFrameControlValues();
767                         return;
768                 }
769         }
770         MoleculeUnlock(mview->mol);
771 }
772
773 void
774 MoleculeView::OnFrameTextAction(wxCommandEvent &event)
775 {
776         int ival, nframes;
777         wxString str;
778         if (mview == NULL || mview->mol == NULL)
779                 return;
780         MoleculeLock(mview->mol);
781         nframes = MoleculeGetNumberOfFrames(mview->mol);
782         if (nframes != 0) {
783                 str = frameText->GetValue();
784                 ival = atoi((const char *)str.mb_str(WX_DEFAULT_CONV));
785                 if (ival >= 0 && ival < nframes) {
786                         MoleculeSelectFrame(mview->mol, ival, 1);
787                         MoleculeUnlock(mview->mol);
788                         MainViewCallback_display(mview);
789                         UpdateFrameControlValues();
790                         return;
791                 }
792         }
793         MoleculeUnlock(mview->mol);
794 }
795
796 void
797 MoleculeView::OnStopProgressPressed(wxCommandEvent& event)
798 {
799         mview->mol->requestAbortThread = 1;
800 }
801
802 void
803 MoleculeView::OnDocumentModified(wxCommandEvent& event)
804 {
805     int xpos, ypos, rowindex, newypos;
806         if (!mview->freezeScreen) {
807                 if (canvas)
808                         canvas->Refresh();
809                 UpdateFrameControls();
810                 MoleculeLock(mview->mol);
811         
812         //  Get the current row/column value; if the table index is the same, keep the
813         //  displayed position as close as possible
814         listctrl->GetScrollPosition(&xpos, &ypos);
815         if (mview->cachedTableIndex != mview->tableIndex) {
816             xpos = ypos = 0;  /*  Different table is shown; should reset the view  */
817         } else {
818             if (mview->tableIndex >= kMainViewAtomTableIndex && mview->tableIndex <= kMainViewImproperTableIndex) {
819                 //  Atom, Bond, Angle, Dihedral, Improper; use the cache
820                 int count = IntGroupGetCount(mview->tableCache);
821                 if (ypos < count)
822                     rowindex = IntGroupGetNthPoint(mview->tableCache, ypos);
823                 else if (count > 0)
824                     rowindex = IntGroupGetNthPoint(mview->tableCache, count - 1);
825                 else rowindex = 0;
826             } else rowindex = ypos;
827         }
828                 MainView_refreshTable(mview);
829         if (mview->tableIndex >= kMainViewAtomTableIndex && mview->tableIndex <= kMainViewImproperTableIndex) {
830             int ic, is, ie, i;
831             ic = IntGroupGetIntervalCount(mview->tableCache);
832             newypos = 0;
833             for (i = 0; i < ic; i++) {
834                 is = IntGroupGetStartPoint(mview->tableCache, i);
835                 ie = IntGroupGetEndPoint(mview->tableCache, i);
836                 if (rowindex < ie) {
837                     if (rowindex >= is)
838                         newypos += (rowindex - is);
839                     break;
840                 }
841                 newypos += ie - is;
842             }
843         } else newypos = rowindex;
844         listctrl->SetScrollPosition(xpos, newypos);
845                 MoleculeUnlock(mview->mol);
846         }
847         
848         if (mview->tableIndex == kMainViewParameterTableIndex && mview->mol->parameterTableSelectionNeedsClear) {
849                 /*  Clear parameter selection if necessary  */
850                 MainViewCallback_setTableSelection(mview, NULL);
851                 mview->mol->parameterTableSelectionNeedsClear = 0;
852         }
853         
854 /*      printf("MoleculeView::OnDocumentModified invoked\n"); */
855         event.Skip();  /*  Continue processing of the notification  */
856 }
857
858 void
859 MoleculeView::OnScriptMenuModified(wxCommandEvent& event)
860 {
861         wxGetApp().UpdateScriptMenu(frame->GetMenuBar());
862         event.Skip();
863 }
864
865 void
866 MoleculeView::OnChar(wxKeyEvent &event)
867 {
868         int code = event.GetKeyCode();
869         int mode = 0;
870         bool noMod = ((event.GetModifiers() | wxMOD_SHIFT) == wxMOD_SHIFT);
871 //      MyAppCallback_showScriptMessage("MoleculeView::OnChar invoked\n");
872         if (code == WXK_BACK || code == WXK_DELETE || code == 0x7f || code == 8) {
873                 MoleculeLock(mview->mol);
874                 MainView_delete(mview);
875                 MoleculeUnlock(mview->mol);
876         } else if (noMod && (code == 'r' || code == 'R'))
877                 mode = kTrackballRotateMode;
878         else if (noMod && (code == 't' || code == 'T'))
879                 mode = kTrackballTranslateMode;
880         else if (noMod && (code == 's' || code == 'S'))
881                 mode = kTrackballScaleMode;
882         else if (noMod && (code == ' '))
883                 mode = kTrackballSelectionMode;
884         else if (noMod && (code == 'b' || code == 'B'))
885                 mode = kTrackballCreateMode;
886         else if (noMod && (code == 'e' || code == 'E'))
887                 mode = kTrackballEraseMode;
888         else {
889                 event.Skip();
890                 return;
891         }
892         if (mode > 0) {
893                 MainView_setMode(mview, mode);
894                 MainViewCallback_selectMatrixCellForMode(mview, mode);
895         }
896 }
897
898 void
899 MoleculeView::SelectTable(int idx)
900 {
901         if (idx >= 0 && idx < listmenu->GetCount() && idx != mview->tableIndex) {
902                 isRebuildingTable = true;
903                 if (MainView_createColumnsForTableAtIndex(mview, idx) == 0) {
904                         /*  Invalid menu item  */
905                         listmenu->SetSelection(mview->tableIndex);
906                         isRebuildingTable = false;
907                         return;
908                 }
909                 listmenu->SetSelection(idx);
910                 isRebuildingTable = false;
911                 MoleculeLock(mview->mol);
912                 MainView_refreshTable(mview);
913                 MoleculeUnlock(mview->mol);
914                 if (idx >= kMainViewParameterTableIndex) {
915                         MainViewCallback_setTableSelection(mview, NULL);
916                         if (idx == kMainViewParameterTableIndex) {
917                                 /*  Not sure whether it is appropriate rebuilding MDArena 
918                                     *every time* the parameter table is opened...  */
919                                 MoleculePrepareMDArena(mview->mol, 1, NULL);
920                         }
921                 }
922         }
923 }
924
925 void
926 MoleculeView::OnSelectTable(wxCommandEvent &event)
927 {
928         if (!isRebuildingTable) {
929                 MoleculeLock(mview->mol);
930                 SelectTable(listmenu->GetSelection());
931                 MoleculeUnlock(mview->mol);
932         }
933 }
934
935 void
936 MoleculeView::OnLeftDClickInListCtrl(wxMouseEvent &event)
937 {
938         listctrl->OnLeftDClick(event);
939         if (mview->tableIndex >= kMainViewBondTableIndex && mview->tableIndex <= kMainViewImproperTableIndex /* && mview->mol->par != NULL */ ) {
940                 int row, col, i;
941                 char indices[64], names[64], types[64], value[20], partypes[64], params[3][20];
942         const char *ptype;
943         char *parstr;
944                 wxPoint pos = event.GetPosition();
945                 if (!listctrl->FindItemAtPosition(pos, &row, &col) || col < 4)
946                         return;
947                 /*  Start editing the local parameter; open a separate dialog  */
948                 MainView_valueForTable(mview, 0, row, indices, sizeof indices);
949                 MainView_valueForTable(mview, 1, row, names, sizeof names);
950                 MainView_valueForTable(mview, 2, row, types, sizeof types);
951                 MainView_valueForTable(mview, 3, row, value, sizeof value);
952                 MainView_valueForTable(mview, 4, row, partypes, sizeof partypes);
953                 for (i = 0; i < 3; i++) {
954                         MainView_valueForTable(mview, 5 + i, row, params[i], sizeof(params[0]));                        
955                 }
956                 switch (mview->tableIndex) {
957                         case kMainViewBondTableIndex: ptype = "bond"; break;
958                         case kMainViewAngleTableIndex: ptype = "angle"; break;
959                         case kMainViewDihedralTableIndex: ptype = "dihedral"; break;
960                         case kMainViewImproperTableIndex: ptype = "improper"; break;
961                         default: return;
962                 }
963                 asprintf(&parstr, "%s %s %s", params[0], params[1], params[2]);
964                 MolActionCreateAndPerform(mview->mol, SCRIPT_ACTION("sssssss"), "cmd_edit_local_parameter_in_mainview", ptype, indices, names, types, value, partypes, parstr);
965         free(parstr);
966         }
967 }
968
969 void
970 MoleculeView::OnActivate(wxActivateEvent &event)
971 {
972         if (!event.GetActive()) {
973                 if (listctrl != NULL)
974                         listctrl->EndEditText(true);
975         }
976         event.Skip();
977 }
978
979 void
980 MoleculeView::OnMoleculeReplaced()
981 {
982         Molecule *mol = ((MyDocument *)GetDocument())->GetMolecule();
983         if (mol == NULL && mview == NULL)
984                 return;
985         if (mol != NULL && mol->mview == mview) {
986                 /*  Clear internal cache  */
987                 MainView_refreshCachedInfo(mol->mview);
988                 return;
989         }
990         if (mview != NULL)
991                 MainView_setViewObject(mview, NULL);
992         if (mol != NULL) {
993                 int tableIndex;
994                 mview = mol->mview;
995                 MainView_setViewObject(mview, this);
996                 /*  Force updating the table  */
997                 tableIndex = mview->tableIndex;
998                 mview->tableIndex = -1;
999                 SelectTable(tableIndex);
1000         }
1001 }
1002
1003 void
1004 MoleculeView::EnableProgressIndicator(bool flag)
1005 {
1006         progress->SetEnabled(flag);
1007 }
1008
1009 #pragma mark ====== MyListCtrl data source ======
1010
1011 int
1012 MoleculeView::GetItemCount(MyListCtrl *ctrl)
1013 {
1014         return MainView_numberOfRowsInTable(mview);
1015 }
1016
1017 wxString
1018 MoleculeView::GetItemText(MyListCtrl *ctrl, long row, long column) const
1019 {
1020         char buf[128];
1021         MainView_valueForTable(mview, column, row, buf, sizeof buf);
1022         wxString *str = new wxString(buf, WX_DEFAULT_CONV);
1023         return *str;
1024 }
1025
1026 int
1027 MoleculeView::SetItemText(MyListCtrl *ctrl, long row, long column, const wxString &value)
1028 {
1029         MainView_setValueForTable(mview, column, row, value.mb_str(WX_DEFAULT_CONV));
1030         return 0;
1031 }
1032
1033 void
1034 MoleculeView::DragSelectionToRow(MyListCtrl *ctrl, long row)
1035 {
1036         MainView_dragTableSelectionToRow(mview, row);
1037 }
1038
1039 bool
1040 MoleculeView::IsItemEditable(MyListCtrl *ctrl, long row, long column)
1041 {
1042         return MainView_isTableItemEditable(mview, column, row);
1043 }
1044
1045 bool
1046 MoleculeView::IsDragAndDropEnabled(MyListCtrl *ctrl, long row)
1047 {
1048         /*  Only enabled for the atom table  */
1049         return (MainView_tableType(mview) == 0);
1050 }
1051
1052 void
1053 MoleculeView::OnSelectionChanged(MyListCtrl *ctrl)
1054 {
1055         MainView_setSelectionFromTable(mview);
1056 }
1057
1058 int
1059 MoleculeView::SetItemColor(MyListCtrl *ctrl, long row, long col, float *fg, float *bg)
1060 {
1061         if (mview != NULL && mview->mol != NULL) {
1062                 return MainView_setColorForTable(mview, col, row, fg, bg);
1063         /*
1064                 if (mview->tableIndex == kMainViewParameterTableIndex && col == -1) {
1065                         int src = ParameterTableGetItemSource(mview->mol->par, row);
1066                         if (src == -2) {  // separator line 
1067                                 bg[0] = bg[1] = bg[2] = 0.6;
1068                                 return 2;
1069                         } else if (src == -1) { //  undefined parameters
1070                                 bg[0] = 1.0;
1071                                 bg[1] = bg[2] = 0.2;
1072                                 return 2;
1073                         } else if (src == 0) {  //  local parameter
1074                                 bg[0] = bg[1] = 1.0;
1075                                 bg[2] = 0.6;
1076                                 return 2;
1077                         }
1078                 } else if (mview->tableIndex > 0 && mview->tableIndex < 5) {
1079                         return MainView_setColorForTable(mview, col, row, fg, bg);
1080                 }
1081         */
1082         }
1083         return 0;
1084 }
1085
1086 bool
1087 MoleculeView::IsRowSelectable(MyListCtrl *ctrl, long row)
1088 {
1089     return MainView_isRowSelectable(mview, row);
1090 }
1091
1092 #pragma mark ====== Plain C interface ======
1093
1094 int
1095 MainViewCallback_modifierFlags(void *eventRef)
1096 {
1097         int flags = 0;
1098         unsigned modifiers;
1099         wxMouseState state = ::wxGetMouseState();
1100         modifiers = 0;
1101         if (state.ShiftDown())
1102           flags |= kShiftKeyMask;
1103         if (state.AltDown())
1104           flags |= kAltKeyMask;
1105         return flags;
1106 }
1107
1108 int
1109 MainViewCallback_clickCount(void *eventRef)
1110 {
1111   wxMouseEvent *mevent = (wxMouseEvent *)eventRef;
1112   if (mevent != NULL) {
1113     if (mevent->LeftDClick())
1114       return 2;
1115     else if (mevent->LeftDown() || mevent->LeftUp())
1116       return 1;
1117     else return 0;
1118   }
1119   return 0;
1120 }
1121
1122 void
1123 MainViewCallback_lockFocus(MainView *mview)
1124 {
1125         if (mview != NULL && mview->ref != NULL)
1126           ((MoleculeView *)(mview->ref))->canvas->SetCurrent();
1127 }
1128
1129 void
1130 MainViewCallback_unlockFocus(MainView *mview)
1131 {
1132   //    if (mview != NULL && mview->ref != NULL)
1133   //  [[(MyWindowController *)(mview->ref) myOpenGLView] unlockFocus];
1134 }
1135
1136 void
1137 MainViewCallback_frame(MainView *mview, float *rect)
1138 {
1139         if (mview != NULL && mview->ref != NULL) {
1140           int width, height;
1141           ((MoleculeView *)(mview->ref))->canvas->GetClientSize(&width, &height);
1142           rect[0] = rect[1] = 0.0;
1143           rect[2] = width;
1144           rect[3] = height;
1145         } else {
1146                 rect[0] = rect[1] = rect[2] = rect[3] = 0.0;
1147         }
1148 }
1149
1150 float
1151 MainViewCallback_getContentScaleFactor(MainView *mview)
1152 {
1153     if (gUseGUI && mview != NULL && mview->ref != NULL) {
1154         return ((MoleculeView *)(mview->ref))->canvas->GetContentScaleFactor();
1155     } else return 1.0;
1156 }
1157
1158
1159 void
1160 MainViewCallback_display(MainView *mview)
1161 {
1162         if (gUseGUI && mview != NULL && mview->ref != NULL) {
1163                 wxWindow *canvas = ((MoleculeView *)(mview->ref))->canvas;
1164                 canvas->Refresh();
1165                 canvas->Update();
1166         }
1167 }
1168
1169 void
1170 MainViewCallback_makeFront(MainView *mview)
1171 {
1172         if (gUseGUI && mview != NULL && mview->ref != NULL) {
1173                 ((MoleculeView *)(mview->ref))->GetFrame()->Raise();
1174         }
1175 }
1176
1177 void
1178 MainViewCallback_setNeedsDisplay(MainView *mview, int flag)
1179 {
1180   if (gUseGUI && mview != NULL && mview->ref != NULL) {
1181     if (flag)
1182       ((MoleculeView *)(mview->ref))->canvas->Refresh();
1183   }
1184 }
1185
1186 void
1187 MainViewCallback_updateCanvas(MainView *mview)
1188 {
1189     if (gUseGUI && mview != NULL && mview->ref != NULL) {
1190         ((MoleculeView *)(mview->ref))->canvas->Update();
1191     }
1192 }
1193
1194 void
1195 MainViewCallback_setKeyboardFocus(MainView *mview)
1196 {
1197         if (gUseGUI && mview != NULL && mview->ref != NULL) {
1198                 ((MoleculeView *)(mview->ref))->canvas->SetFocus();
1199         }
1200 }
1201
1202 void
1203 MainViewCallback_enableToggleButton(MainView *mview, int mode, int flag)
1204 {
1205         if (mode >= kTrackballRotateMode && mode <= kTrackballEraseMode)
1206                 ((MoleculeView *)(mview->ref))->GetToggleButtonAtIndex(mode - kTrackballRotateMode)->Enable(flag);
1207 }
1208
1209 void
1210 MainViewCallback_clearLabels(MainView *mview)
1211 {
1212         return;
1213 /*
1214         if (mview != NULL && mview->ref != NULL) {
1215                 id view = [(MyWindowController *)(mview->ref) myOverlayView];
1216                 NSRect bounds = [view bounds];
1217                 [view lockFocus];
1218                 NSEraseRect(bounds);
1219                 [[NSColor cyanColor] set];
1220                 bounds.origin.x = bounds.size.width / 2;
1221                 NSFrameRect(bounds);
1222                 [view unlockFocus];
1223                 [view setNeedsDisplay: YES];
1224         }
1225 */
1226 }
1227
1228 void
1229 MainViewCallback_drawLabel(MainView *mview, const float *pos, const char *label)
1230 {
1231         return;
1232 /*
1233         if (mview != NULL && mview->ref != NULL) {
1234                 id view = [(MyWindowController *)(mview->ref) myOverlayView];
1235                 NSString *s = [NSString stringWithUTF8String: label];
1236                 NSDictionary *attr = [(MyWindowController *)(mview->ref) labelAttributes];
1237                 [view lockFocus];
1238                 [s drawAtPoint: NSMakePoint(pos[0], pos[1]) withAttributes: attr];
1239                 [view unlockFocus];
1240                 [view setNeedsDisplay: YES];
1241         }
1242 */
1243 }
1244
1245 void
1246 MainViewCallback_drawInfoText(MainView *mview, const char *label)
1247 {
1248         if (gUseGUI && mview != NULL && mview->ref != NULL) {
1249                 wxString labelstr(label, WX_DEFAULT_CONV);
1250                 ((MoleculeView *)(mview->ref))->infotext->SetLabel(labelstr);
1251         }
1252 }
1253
1254 int
1255 MainViewCallback_mouseCheck(MainView *mview)
1256 {
1257         return 0;
1258 }
1259
1260 void
1261 MainViewCallback_selectMatrixCellForMode(MainView *mview, int mode)
1262 {
1263         if (mview != NULL && mview->ref != NULL)
1264                 ((MoleculeView *)(mview->ref))->SelectButtonForMode(mode);
1265 }
1266
1267 //int
1268 //MainViewCallback_getTag(MainView *mview)
1269 //{
1270   //    if (mview != NULL && mview->ref != NULL)
1271   //    return [(MyWindowController *)(mview->ref) myTag];
1272   //else return -1;
1273 //}
1274
1275 MainView *
1276 MainViewCallback_viewWithTag(int tag)
1277 {
1278         wxList &doclist = wxGetApp().DocManager()->GetDocuments();
1279         wxList::iterator iter;
1280         int i = 0;
1281         for (i = 0, iter = doclist.begin(); iter != doclist.end(); ++i, ++iter) {
1282                 if (i == tag) {
1283                         return ((MoleculeView *)(((MyDocument *)(*iter))->GetFirstView()))->mview;
1284                 }
1285         }
1286         return NULL;
1287   //wxList::compatibility_iterator iter = doclist.Item(tag);
1288   //if (iter != NULL)
1289   //  return (MyDocument *)(*iter)->GetFirstView()->mview;
1290   //else
1291 //    return NULL;
1292   //    int i;
1293   //    if (sMyWindowControllers == nil)
1294   //            return NULL;
1295   //    for (i = [sMyWindowControllers count] - 1; i >= 0; i--) {
1296   //            id obj = [sMyWindowControllers objectAtIndex: i];
1297   //    if ([obj myTag] == tag)
1298   //                    return [obj mainView];
1299   //    }
1300   //    return NULL;
1301 }
1302
1303 MainView *
1304 MainViewCallback_activeView(void)
1305 {
1306         if (sActiveViews.GetCount() == 0)
1307                 return NULL;
1308         else {
1309                 MoleculeView *aview = sActiveViews.Item(0);
1310                 return aview->mview;
1311         }
1312 //      MoleculeView *cview = (MoleculeView *)(wxGetApp().DocManager()->GetCurrentView());
1313 //      if (cview == NULL)
1314 //              return NULL;
1315 //      else
1316 //              return cview->mview;
1317 }
1318
1319 /*
1320 MainView *
1321 MainViewCallback_newFromFile(const char *fname)
1322 {
1323   wxDocument *doc;
1324   wxDocManager *manager = wxGetApp().DocManager();
1325   if (fname == NULL || *fname == 0) {
1326     doc = manager->CreateDocument(wxT(""), wxDOC_NEW);
1327   } else {
1328     wxString fnamestr(fname, wxConvFile);
1329     doc = manager->CreateDocument(fnamestr, wxDOC_SILENT);
1330   }
1331   return MainViewCallback_activeView();
1332 }
1333 */
1334
1335 int
1336 MainViewCallback_importFromFile(MainView *mview, const char *fname)
1337 {
1338   MyDocument *doc;
1339   if (mview != NULL && mview->ref != NULL && (doc = (((MoleculeView *)(mview->ref))->MolDocument())) != NULL) {
1340     wxString fnamestr(fname, wxConvFile);
1341     // doc->importFromFile(fnamestr);
1342     MainViewCallback_setNeedsDisplay(mview, 1);
1343     return 1;
1344   }
1345   return 0;
1346 }
1347
1348 void
1349 MainViewCallback_getFilename(MainView *mview, char *buf, int bufsize)
1350 {
1351   MyDocument *doc;
1352   if (mview != NULL && mview->ref != NULL && (doc = (((MoleculeView *)(mview->ref))->MolDocument())) != NULL) {
1353     wxString fname;
1354     fname = doc->GetFilename();
1355     strncpy(buf, (const char*)fname.mb_str(wxConvFile), bufsize - 1);
1356     buf[bufsize - 1] = 0;
1357   } else {
1358     buf[0] = 0;
1359   }
1360 }
1361
1362 /*
1363 void
1364 MainViewCallback_moleculeReplaced(MainView *mview, struct Molecule *mol)
1365 {
1366         if (mview != NULL && mview->ref != NULL) {
1367                 MyDocument *doc = ((MoleculeView *)(mview->ref))->MolDocument();
1368                 if (doc != NULL)
1369                         doc->SetMolecule(mol);
1370                 MyListCtrl *listctrl = ((MoleculeView *)(mview->ref))->GetListCtrl();
1371                 if (listctrl != NULL)
1372                         listctrl->SetDataSource((MoleculeView *)(mview->ref));
1373         }
1374 }
1375 */
1376
1377 typedef struct Label {
1378   //    StringTexture *tex;
1379 } Label;
1380
1381 struct Label *
1382 MainViewCallback_newLabel(MainView *mview, const char *message, float fontsize, const float *forecolor, const float *backcolor)
1383 {
1384   /*
1385         Label *label;
1386         NSDictionary *attr;
1387         NSColor *textColor, *boxColor;
1388         label = (Label *)malloc(sizeof(Label));
1389         if (label == NULL)
1390                 return NULL;
1391         memset(label, 0, sizeof(Label));
1392 //      MainViewCallback_lockFocus(mview);
1393         if (forecolor != NULL)
1394                 textColor = [NSColor colorWithDeviceRed: forecolor[0] green: forecolor[1] blue: forecolor[2] alpha: forecolor[3]];
1395         else
1396                 textColor = [NSColor whiteColor];
1397         if (backcolor != NULL)
1398                 boxColor = [NSColor colorWithDeviceRed: backcolor[0] green: backcolor[1] blue: backcolor[2] alpha: backcolor[3]];
1399         else
1400                 boxColor = [NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:0.0f];
1401         attr = [NSDictionary dictionaryWithObjectsAndKeys: [NSFont userFontOfSize: fontsize], NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil];
1402         label->tex = [[StringTexture alloc] 
1403                 initWithString: [NSString stringWithUTF8String: message] 
1404                 withAttributes: attr
1405                 withTextColor: textColor
1406                 withBoxColor: boxColor
1407                 withBorderColor: [NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:0.0f]];
1408 //      MainViewCallback_unlockFocus(mview);
1409         return label;
1410   */
1411         return NULL;
1412 }
1413
1414 void
1415 MainViewCallback_releaseLabel(struct Label *label)
1416 {
1417   /*    if (label != NULL) {
1418                 [label->tex release];
1419                 free(label);
1420         }
1421   */
1422 }
1423
1424 void
1425 MainViewCallback_drawLabelAtPoint(struct Label *label, const float *pos)
1426 {
1427   /*
1428         if (label != NULL && pos != NULL) {
1429         //      GLint matrixMode;
1430                 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
1431         //      glGetIntegerv (GL_MATRIX_MODE, &matrixMode);
1432         //      glMatrixMode(GL_MODELVIEW);
1433         //      glPushMatrix();
1434         //      glTranslatef(0.0f, 0.0f, pos[3]);
1435                 [label->tex drawAtPoint: NSMakePoint(pos[0], pos[1]) withDepth: pos[2]];
1436         //      glPopMatrix();
1437         //      glMatrixMode (matrixMode);
1438         }
1439   */
1440 }
1441
1442 void
1443 MainViewCallback_labelSize(struct Label *label, float *outSize)
1444 {
1445   /*    if (label != NULL) {
1446                 NSSize size = [label->tex frameSize];
1447                 if (outSize != NULL) {
1448                         outSize[0] = size.width;
1449                         outSize[1] = size.height;
1450                 }
1451         }
1452   */
1453 }
1454
1455 int
1456 MainViewCallback_exportGraphic(MainView *mview, const char *fname, float scale, int bg_color, int width, int height)
1457 {
1458     if (!gUseGUI)
1459         return 0;
1460         if (mview != NULL && mview->ref != NULL && ((MoleculeView *)(mview->ref))->MolDocument() != NULL) {
1461                 wxString fnamestr(fname, wxConvFile);
1462                 return ((MoleculeView *)(mview->ref))->DoExportGraphic(fnamestr, scale, bg_color, width, height);
1463         }
1464         return -100;
1465 }
1466
1467 #pragma mark ====== Plain C Interface (MyListCtrl) ======
1468
1469 /*  These interface functions are also used for accessing MyListCtrl of GlobalParameterFrame (when mview == NULL) */
1470
1471 static MyListCtrl *
1472 s_MyListCtrlFromMainView(MainView *mview)
1473 {
1474         if (mview == NULL)
1475                 return wxGetApp().GetGlobalParameterListCtrl();
1476         if (mview != NULL && mview->ref != NULL)
1477                 return ((MoleculeView *)(mview->ref))->GetListCtrl();
1478         else return NULL;
1479 }
1480
1481 void
1482 MainViewCallback_selectTable(MainView *mview, int idx)
1483 {
1484         if (mview != NULL && mview->ref != NULL)
1485                 ((MoleculeView *)(mview->ref))->SelectTable(idx);
1486 }
1487
1488 int
1489 MainViewCallback_numberOfTableColumns(MainView *mview)
1490 {
1491         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1492         if (listctrl != NULL)
1493                 return listctrl->GetColumnCount();
1494         else return 0;
1495 }
1496
1497 int
1498 MainViewCallback_addTableColumn(MainView *mview, const char *name, int width, int editable)
1499 {
1500         int idx;
1501         wxString nstr(name, WX_DEFAULT_CONV);
1502         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1503         if (listctrl == NULL)
1504                 return 0;
1505         idx = listctrl->GetColumnCount();
1506         listctrl->InsertColumn(idx, nstr, wxLIST_FORMAT_LEFT);
1507         listctrl->SetColumnWidth(idx, FromFrameDIP(listctrl, width * 10));
1508         return idx;
1509 }
1510
1511 int
1512 MainViewCallback_removeTableColumnAtIndex(MainView *mview, int idx)
1513 {
1514         int ncolumns;
1515         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1516         if (listctrl == NULL)
1517                 return 0;
1518         ncolumns = listctrl->GetColumnCount();
1519         if (idx >= 0 && idx < ncolumns) {
1520                 listctrl->DeleteColumn(idx);
1521                 ncolumns--;
1522         }
1523         return ncolumns;
1524 }
1525
1526 void
1527 MainViewCallback_reloadTableData(MainView *mview)
1528 {
1529         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1530         if (listctrl != NULL)
1531                 listctrl->RefreshTable();
1532 /*      
1533         int nrows = MainView_numberOfRowsInTable(mview);
1534         listctrl->SetItemCount(nrows);
1535         if (nrows > 0)
1536                 listctrl->RefreshItems(0, nrows - 1);
1537 */
1538 }
1539
1540 void
1541 MainViewCallback_setTableSelection(MainView *mview, IntGroup *selection)
1542 {
1543         int i, n, n1, n2;
1544         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1545         if (listctrl == NULL)
1546                 return;
1547         n = 0;
1548         listctrl->EnableSelectionChangeNotification(false);
1549         if (selection != NULL) {
1550                 for (i = 0; (n1 = IntGroupGetStartPoint(selection, i)) >= 0; i++) {
1551                         n2 = IntGroupGetEndPoint(selection, i);
1552                         while (n < n1) {
1553                                 listctrl->SetItemState(n, 0, wxLIST_STATE_SELECTED);
1554                                 n++;
1555                         }
1556                         while (n < n2) {
1557                                 listctrl->SetItemState(n, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
1558                                 n++;
1559                         }
1560                 }
1561         }
1562         listctrl->RefreshTable();
1563         n1 = MainView_numberOfRowsInTable(mview);
1564         while (n < n1) {
1565                 listctrl->SetItemState(n, 0, wxLIST_STATE_SELECTED);
1566                 n++;
1567         }
1568 //      listctrl->RefreshItems(0, n1 - 1);
1569         {
1570                 //  EVT_LIST_ITEM_SELECTED is sent by wxPostEvent rather than ProcessEvent,
1571                 //  so re-enable of table selection event should also be sent by wxPostEvent.
1572                 //  Otherwise, the enable flag is set to true _before_ EVT_LIST_ITEM_SELECTED is sent.
1573                 wxCommandEvent myEvent(MyListCtrlEvent, MyListCtrlEvent_enableTableSelectionNotification);
1574                 wxPostEvent(listctrl, myEvent);
1575         }
1576 }
1577
1578 IntGroup *
1579 MainViewCallback_getTableSelection(MainView *mview)
1580 {
1581         int i, n;
1582         IntGroup *ig;
1583         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1584         if (listctrl == NULL)
1585                 return NULL;
1586         ig = IntGroupNew();
1587         n = MainView_numberOfRowsInTable(mview);
1588         for (i = 0; i < n; i++) {
1589                 if (listctrl->GetItemState(i, wxLIST_STATE_SELECTED) != 0)
1590                         IntGroupAdd(ig, i, 1);
1591         }
1592         return ig;
1593 }
1594
1595 void
1596 MainViewCallback_showTable(MainView *mview)
1597 {
1598   //    if (mview != NULL && mview->ref != NULL) {
1599   //    MyDocument *doc = (MyDocument *)[(id)(mview->ref) document];
1600   //    [doc showTable: doc];
1601   //}
1602 }
1603
1604 void
1605 MainViewCallback_hideTable(MainView *mview)
1606 {
1607   //    if (mview != NULL && mview->ref != NULL) {
1608   //    MyDocument *doc = (MyDocument *)[(id)(mview->ref) document];
1609   //            [doc hideTable: doc];
1610   //    }
1611 }
1612
1613 void
1614 MainViewCallback_ensureVisible(MainView *mview, int row)
1615 {
1616         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1617         if (listctrl != NULL)
1618                 listctrl->EnsureVisible(row);
1619 }
1620
1621 void
1622 MainViewCallback_startEditText(MainView *mview, int row, int column)
1623 {
1624         MyListCtrl *listctrl = s_MyListCtrlFromMainView(mview);
1625         if (listctrl != NULL)
1626                 listctrl->StartEditText(row, column);
1627 }
1628