OSDN Git Service

MO surface display is improved
[molby/Molby.git] / Scripts / commands.rb
1 # coding: utf-8
2 #
3 #  commands.rb
4 #
5 #  Created by Toshi Nagata on 2008/06/28.
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 class Molecule
18
19   def cmd_assign_residue
20     sel = self.selection
21         if sel.length == 0
22           sel = self.atom_group
23         end
24         atoms = sel.inspect.sub!("IntGroup[", "").sub!("]", "")
25     hash = Dialog.run("Assign Residue") {
26           layout(2,
27                 item(:text, :title=>"New residue name/number\n(like \"RES.1\")\nfor atoms #{atoms}"),
28             item(:textfield, :width=>120, :tag=>"residue"))
29     }
30     if hash[:status] == 0
31           residue = hash["residue"]
32           assign_residue(sel, residue)
33         end
34   end
35
36   def cmd_offset_residue
37     sel = self.selection
38         if sel.length == 0
39           sel = self.atom_group
40         end
41         atoms = sel.inspect.sub!("IntGroup[", "").sub!("]", "")
42     hash = Dialog.run("Offset Residues") {
43           layout(2,
44                 item(:text, :title=>"Offset residue number:\nfor atoms #{atoms}"),
45             item(:textfield, :width=>120, :tag=>"offset"))
46     }
47         if hash[:status] == 0
48           offset = hash["offset"].to_i
49           offset_residue(sel, offset)
50         end
51   end
52    
53   def cmd_sort_by_residue
54     sel = self.selection
55         if sel.length == 0
56           sel = self.atom_group
57         end
58         sorted = sel.sort_by { |i| [self.atoms[i].res_seq, i] }
59         ary = []
60         j = 0
61         (0...natoms).each { |i|
62           if sel.include?(i)
63             ary << sorted[j]
64                 j += 1
65                 break if j >= sorted.length
66           else
67             ary << i
68           end
69         }
70         self.renumber_atoms(ary)
71   end
72
73   def cmd_delete_frames
74     n = nframes
75     return if n == 0
76         hash = Dialog.run("Delete Frames") {
77           layout(2,
78             item(:text, :title=>"Start"),
79             item(:textfield, :width=>120, :tag=>"start", :value=>"0"),
80                 item(:text, :title=>"End"),
81                 item(:textfield, :width=>120, :tag=>"end", :value=>(n - 1).to_s),
82                 item(:text, :title=>"Keeping frames every..."),
83                 -1,
84                 item(:text, :title=>"Step"),
85                 item(:textfield, :width=>120, :tag=>"step", :value=>"0"))
86         }
87         if hash[:status] == 0
88           sframe = Integer(hash["start"])
89           eframe = Integer(hash["end"])
90           step = Integer(hash["step"])
91           return if sframe > eframe
92           eframe = n - 1 if eframe >= n
93           fgroup = IntGroup[sframe..eframe]
94           if step > 0
95             while sframe <= eframe
96                   fgroup.delete(sframe)
97                   sframe += step
98                 end
99           end
100           remove_frames(fgroup)
101         end
102   end
103
104   def cmd_reverse_frames
105     n = nframes
106     return if n == 0
107         hash = Dialog.run("Reverse Frames") {
108           layout(2,
109             item(:text, :title=>"Start"),
110             item(:textfield, :width=>120, :tag=>"start", :value=>"0"),
111                 item(:text, :title=>"End"),
112                 item(:textfield, :width=>120, :tag=>"end", :value=>(n - 1).to_s))
113         }
114         if hash[:status] == 0
115           sframe = Integer(hash["start"])
116           eframe = Integer(hash["end"])
117           return if sframe > eframe
118           eframe = n - 1 if eframe >= n
119           frames = (0...sframe).to_a + (sframe..eframe).to_a.reverse + ((eframe + 1)...n).to_a
120           reorder_frames(frames)
121         end
122   end
123
124   def cmd_concat_frames
125       n = nframes
126       return if n == 0
127       if Molecule.list.length == 1
128           message_box("No other molecule.", "", :ok)
129           return
130       end
131       ls = (0...Molecule.list.length).select { |i|
132           m = Molecule[i]
133           if m == self || m.natoms != self.natoms
134               false
135               else
136               if nil != (0...m.natoms).find { |n| m.atoms[n].atomic_number != self.atoms[n].atomic_number }
137                   false
138                   else
139                   true
140               end
141           end
142       }
143       if ls.length == 0
144           message_box("No molecule has the same atomic sequence as the present one.", "", :ok)
145           return
146       end
147       labels = []
148       label_hash = Hash.new   #  Check if documents with the same name are present
149       ls.each_with_index { |i, idx|  #  i is an index to Molecule, idx is an index to ls
150           m = Molecule[i]
151           label = m.name
152           idx_h = label_hash[label]  #  If non-nil, then duplicate name
153           label_hash[label] = idx
154           if idx_h
155               if labels[idx_h] == label
156                   labels[idx_h] = label + " (" + Molecule[ls[idx_h]].dir + ")"
157               end
158               label = label + " (" + Molecule[i].dir + ")"
159           end
160           labels.push(label)
161       }
162       nf = Molecule[ls[0]].nframes
163       hash = Dialog.run("Concatenate Frames") {
164           layout(2,
165                  item(:text, :title=>"From Molecule:"),
166                  item(:popup, :subitems=>labels, :tag=>"mol", :width=>240,
167                       :action=>lambda { |it|
168                       nf = Molecule[ls[it[:value]]].nframes
169                       set_attr("start_title", :title=>"Start (0-#{nf})")
170                       set_attr("end_title", :title=>"End (0-#{nf})")
171                       }),
172                  item(:radio, :title=>"All Frames", :tag=>"all_frames", :value=>1,
173                       :action=>lambda { |it|
174                       flag = (it[:value] == 0)
175                       set_attr("start_frame", :enabled=>flag)
176                       set_attr("end_frame", :enabled=>flag)
177                       }), -1,
178                  item(:radio, :title=>"Select Frames", :tag=>"select_frames",
179                       :action=>lambda { |it|
180                       flag = (it[:value] != 0)
181                       set_attr("start_frame", :enabled=>flag)
182                       set_attr("end_frame", :enabled=>flag)
183                       }), -1,
184                  item(:text, :title=>"Start (0-#{nf})", :tag=>"start_title"),
185                  item(:textfield, :value=>"0", :tag=>"start_frame", :enabled=>false),
186                  item(:text, :title=>"End (0-#{nf})", :tag=>"end_title"),
187                  item(:textfield, :value=>"0", :tag=>"end_frame", :enabled=>false))
188                  radio_group("all_frames", "select_frames")
189       }
190       if hash[:status] == 0
191           idx_h = Integer(hash["mol"])
192           m = Molecule[ls[idx_h]]
193           f = m.frame  #  Save the current frame number
194           nf = m.nframes
195           if hash["all_frame"] != 0
196               f1 = 0
197               f2 = nf - 1
198               else
199               f1 = Integer(hash["start_frame"])
200               f2 = Integer(hash["end_frame"])
201               f1 = 0 if f1 < 0
202               f1 = nf - 1 if f1 > nf - 1
203               f2 = 0 if f2 < 0
204               f2 = nf - 1 if f2 > nf - 1
205           end
206           if f1 <= f2
207               a = (f1..f2).to_a
208               else
209               a = (f2..f1).to_a.reverse
210           end
211           prop = m.property_names
212           na = m.natoms
213           a.each { |n|
214               self.create_frame()
215               m.select_frame(n)
216               na.times { |i|
217                   self.atoms[i].r = m.atoms[i].r
218               }
219               prop.each { |pr|
220                   self.set_property(pr, m.get_property(pr))
221               }
222           }
223       end
224   end
225
226   def cmd_extra_properties
227     mol = self
228         get_count = lambda { |it| mol.nframes }
229         get_value = lambda { |it, row, col|
230           if col == 0
231             row.to_s
232           else
233             sprintf("%.8f", mol.get_property(col - 1, row)) rescue ""
234           end
235     }
236     mol.open_auxiliary_window("Extra Props", :resizable=>true, :has_close_box=>true) {
237           names = nil
238           @on_document_modified = lambda { |m|
239             if (n = m.property_names) != names
240                   names = n
241                   col = [["frame", 40]] + names.map { |nn| [nn, 120] }
242                   item_with_tag("table")[:columns] = col
243                 end
244                 item_with_tag("table")[:refresh] = true
245           }
246           layout(1,
247                 item(:table, :width=>320, :height=>300, :flex=>[0,0,0,0,1,1], :tag=>"table",
248                   :on_count=>get_count,
249                   :on_get_value=>get_value),
250             :flex=>[0,0,0,0,1,1]
251           )
252           set_min_size(320, 200)
253           # listen(mol, "documentModified", on_document_modified)
254           # listen(mol, "documentWillClose", lambda { |m| close } )
255           @on_document_modified.call(mol)
256           show
257         }
258   end
259   
260   def cmd_show_energy
261     wave = [0.0, 0.0]
262     cur = 0
263     mol = self
264     wave_min = lambda { m = 1e8; wave.each { |x| if x != 0.0 && x < m; m = x; end }; m }
265     wave_max = lambda { m = -1e8; wave.each { |x| if x != 0.0 && x > m; m = x; end }; m }
266     d = open_auxiliary_window("Energy", :resizable=>true, :has_close_box=>true) {
267     graph_item = nil   #  Forward declaration
268     target_mol = nil
269     draw_graph = lambda { |it|
270       clear
271       f = graph_item[:frame]
272       draw_rectangle(0, 0, f[2], f[3])
273       width = f[2] - 25
274       height = f[3] - 25
275       draw_line(16, 0, 16, height + 12, width + 20, height + 12)
276       xx = yy = nil
277       min = wave_min.call
278       max = wave_max.call
279       h = max - min
280       h = (h == 0.0 ? 1.0 : height / h)
281       c = wave.count
282       if c == 0
283         w = 1.0
284       elsif c == 1
285         w = Float(width)
286       else
287         w = Float(width) / (c - 1)
288       end
289       lines = []
290       a = []
291       #  Skip the points that have exactly 0.0 value
292       wave.each_with_index { |d, i|
293         if d != 0.0
294           a.push(i * w + 16)
295           a.push(height - (d - min) * h + 12)
296         end
297         if d == 0.0 || i == wave.length - 1
298           #  End of this curve fragment
299           if a.length == 0
300             #  Do nothing
301           else
302             if a.length == 2
303               if wave.count == 1
304                 #  If wave has only one point, then draw a horizontal line
305                 a.push(a[0] + 16)
306                 a.push(a[1])
307               else
308                 #  Otherwise, draw a zero-length line
309                 a.push(a[0])
310                 a.push(a[1])
311               end
312             end
313             lines.push(a)  #  Store this line fragment
314             a = []
315           end
316         end
317       }
318       lines.each { |a|
319         draw_line(a)
320       }
321       brush(:color=>[0.2, 0.2, 1.0])
322       y = wave[cur] || 0.0
323       if y < min
324         y = min
325       elsif y > max
326         y = max
327       end
328       xx = cur * w + 16
329       yy = height - (y - min) * h + 12
330       draw_ellipse(cur * w + 16, height - (y - min) * h + 12, 6)
331     }
332           @on_document_modified = lambda { |m|
333                 cur = mol.frame
334                 wave.clear
335                 if mol.nframes < 2
336                   wave = [mol.get_property("energy", 0)] * 2
337                 else
338                   wave = (0...mol.nframes).map { |i| mol.get_property("energy", i) }
339                 end
340                 f = graph_item[:frame]
341                 item_with_tag("energy")[:title] = sprintf("Energy = %.10f hartree, Frame = %d", mol.get_property("energy", cur), cur)
342                 graph_item.refresh_rect([0, 0, f[2], f[3]])
343           }
344           layout(1,
345                 item(:text, :title=>"Energy =                 ", :tag=>"energy"),
346                 item(:view, :frame=>[0, 0, 320, 240], :tag=>"graph", :on_paint=>draw_graph, :flex=>[0,0,0,0,1,1]),
347         #       item(:button, :title=>"Update", :action=>doc_modified, :align=>:center, :flex=>[1,1,1,0,0,0]),
348         #       item(:button, :title=>"Close", :action=>proc { hide }, :align=>:center, :flex=>[1,1,1,0,0,0]),
349                 :flex=>[0,0,0,0,1,1]
350           )
351           graph_item = item_with_tag("graph")
352           size = self.size
353           set_min_size(size)
354           set_size(500, 300)
355           @on_document_modified.call(mol)
356           show
357         }
358         self
359   end
360   
361   def cmd_create_surface
362     if @surface_dialog_attr == nil
363           @surface_dialog_attr = Hash.new
364           @surface_dialog_attr["hidden"] = -1
365         end
366     surface_dialog_attr = @surface_dialog_attr
367     mol = self
368         mol.open_auxiliary_window("MO Surface", :resizable=>true, :has_close_box=>true) {
369           tags = ["mo_ao", "mo", "color", "opacity", "threshold", "expand", "grid", "showbox", "reverse", "basis"]
370           motype = mol.get_mo_info(:type)
371           alpha = mol.get_mo_info(:alpha)
372           beta = mol.get_mo_info(:beta)
373           ncomps = mol.get_mo_info(:ncomps)
374           mo_index = 0
375           mo_ao = nil
376           coltable = [[0,0,1], [1,0,0], [0,1,0], [1,1,0], [0,1,1], [1,0,1], [0,0,0], [1,1,1]]
377           mo_ao_items = ["Molecular Orbitals", "Atomic Orbitals"]
378           mo_ao_keys = ["MO", "AO"]
379     if (nbo = mol.instance_eval { @nbo }) != nil
380             nbo.keys.each { |key|
381         if key[0..2] == "AO/"
382           key2 = key[3..-1]
383           name2 = key2
384           if key2 == "NAO"
385             name2 = "Natural Atomic Orbitals"
386           elsif key2 == "NBO"
387             name2 = "Natural Bond Orbitals"
388           elsif key2 == "NHO"
389             name2 = "Natural Hybrid Orbitals"
390           elsif key2 == "NLMO"
391             name2 = "Natural Localized Molecular Orbitals"
392           elsif key2 == "PNAO"
393             name2 = "Pre-orthogonal Natural Atomic Orbitals"
394           elsif key2 == "PNHO"
395             name2 = "Pre-orthogonal Natural Hybrid Orbitals"
396           elsif key2 == "PNBO"
397             name2 = "Pre-orthogonal Natural Bond Orbitals"
398           elsif key2 == "AHO"
399             name2 = "Atomic Hybrid Orbitals"
400           elsif key2 == "LHO"
401             name2 = "Lewis Hybrid Orbitals"
402           elsif key2 == "PLHO"
403             name2 = "Pre-orthogonal Lewis Hybrid Orbitals"
404           elsif key2 == "LPO"
405             name2 = "Localized Property-optimized Orbitals"
406           elsif key2 == "CLPO"
407             name2 = "Chemist's Localized Property-optimized Orbitals"
408           end
409           mo_ao_items.push(name2)
410           mo_ao_keys.push(key2)
411         end
412       }
413           end
414           mult = (motype == "UHF" ? 2 : 1)
415           mo_menu = ["1000 (-00.00000000)"]  #  Dummy entry; create later
416           tabvals = []
417     coeffs_matrix = nil  #  Coefficient matrix for showing surface
418     coeffs = nil   #  Coefficient array for showing surface
419     table_coeffs_matrix = nil  #  Coefficient matrix for table (based on "basis")
420           a_idx_old = -1
421           occ = nil
422           on_update_attr = lambda {
423             tags.each { |tag|
424                   surface_dialog_attr[tag] = item_with_tag(tag)[:value]
425                 }
426                 it = item_with_tag("hide")
427                 case surface_dialog_attr["hidden"]
428                 when -1
429                   it[:title] = "Hide"
430                   it[:enabled] = false
431                 when 0
432                   it[:title] = "Hide"
433                   it[:enabled] = true
434                 when 1
435                   it[:title] = "Show"
436                   it[:enabled] = true
437                 end
438           }
439     update_mo_labels = lambda { |mo_ao_index|
440       m = []
441       if mo_ao_index == 0
442         m = (1..(ncomps * mult)).map { |n|
443           if motype == "UHF"
444             i1 = (n - 1) / 2 + 1
445             i2 = n % 2
446             c1 = (i2 == 0 ? "B" : "A")
447             c2 = (i1 > (i2 == 0 ? beta : alpha) ? "*" : "")
448             else
449             i1 = n
450             i2 = 1
451             c1 = ""
452             if i1 > beta && i1 > alpha
453               c2 = "*"
454               elsif i1 > beta || i1 > alpha
455               c2 = "S"
456               else
457               c2 = ""
458             end
459           end
460           en = mol.get_mo_energy(i1 + (i2 == 0 ? ncomps : 0))
461           sprintf("%d%s%s (%.8f)", i1, c1, c2, en)
462         }
463       elsif mo_ao_index == 1
464         m = []
465         ncomps.times { |i|
466           m[i] = sprintf("%d: %s (%s)", i + 1, tabvals[i][2], mol.atoms[tabvals[i][3]].name)
467         }
468       else
469         m = []
470         key = mo_ao_keys[mo_ao_index]
471         labels = nbo[key + "_L"]
472         if labels == nil && key[0] == ?P
473           labels = nbo[key[1..-1] + "_L"]
474         end
475         ncomps.times { |i|
476           if labels
477             lab = sprintf("%d: %s", i + 1, labels[i])
478           else
479             lab = sprintf("%s%d", key, i + 1)
480           end
481           m[i] = lab
482         }
483       end
484       return m
485     }
486           on_get_value = lambda { |it, row, col|
487     begin
488       if !table_coeffs_matrix || !coeffs_matrix
489         #  Update tabvals
490         tabvals = []
491         bs = value("basis")
492         if bs == 0  #  AO
493           ncomps.times { |i|
494             a_idx, s_idx, label = mol.get_gaussian_component_info(i)
495             if a_idx_old != a_idx
496               a_idx_old = a_idx
497               a = a_idx.to_s
498               n = mol.atoms[a_idx].name
499             else
500               a = n = ""
501             end
502             tabvals.push([a, n, label, a_idx])
503           }
504         else
505           labels = update_mo_labels.call(bs + 1)
506           ncomps.times { |i|
507             label = labels[i]
508             label = label.gsub(/^\d+: /, "")
509             tabvals.push([(i + 1).to_s, "", label, i])
510           }
511         end
512         #  Update coeffs_matrix
513         if !coeffs_matrix
514           #  coeffs_matrix = AO/(orbitals_to_display)
515           if mo_ao == 0
516             m = []
517             ncomps.times { |i|
518               mult.times { |j|
519                 m.push(mol.get_mo_coefficients(i * mult + j + 1))
520               }
521             }
522             coeffs_matrix = LAMatrix.new(m)  #  Matrix AO/MO
523           elsif mo_ao == 1
524             coeffs_matrix = LAMatrix.identity(ncomps)  #  Matrix AO/AO  (identity)
525           else
526             coeffs_matrix = nbo["AO/" + mo_ao_keys[mo_ao]]
527           end
528         end
529         #  m2 = AO/(basis)
530         if bs == 0   #  AO
531           m2 = LAMatrix.identity(ncomps)
532         else
533           m2 = nbo["AO/" + mo_ao_keys[bs + 1]]
534         end
535         #  (basis)/(orbitals_to_display)
536         table_coeffs_matrix = m2.inverse * coeffs_matrix
537       end
538       #  Update coefficient array
539       if !coeffs
540         coeffs = coeffs_matrix.column(mo_index).to_a[0]
541       end
542       if col < 3
543         tabvals[row][col]
544       else
545         sprintf("%.6f", table_coeffs_matrix[mo_index, row])
546       end
547     rescue => e
548       $stderr.write(e.to_s + "\n")
549       $stderr.write(e.backtrace.inspect + "\n")
550     end
551           }
552           h = {"mo"=>nil, "color"=>nil, "opacity"=>nil, "threshold"=>nil, "expand"=>nil, "grid"=>nil, "reverse"=>nil, "basis"=>nil }
553           should_update = true
554           on_action = lambda { |it|
555             tag = it[:tag]
556                 value = it[:value]
557                 if tag == "color" || tag == "opacity"
558                   opac = value("opacity").to_f
559                   opac = 0.0 if opac < 0.0
560                   opac = 1.0 if opac > 1.0
561                   col = value("color")
562                   color = coltable[col] + [opac]
563                   color0 = [1,1,1,opac]
564                   mol.set_surface_attr(:color=>color, :color0=>color0)
565                   h[tag] = value
566         elsif tag == "threshold" || tag == "reverse"
567           thres = item_with_tag("threshold")[:value].to_f
568                   thres = 0.001 if thres >= 0.0 && thres < 0.001
569                   thres = -0.001 if thres <= 0.0 && thres > -0.001
570           if item_with_tag("reverse")[:value] == 1
571             thres = -thres
572           end
573                   mol.set_surface_attr(:thres=>thres)
574                   h[tag] = value
575         elsif tag == "showbox"
576           mol.set_surface_attr(:showbox=>value.to_i)
577           h[tag] = value
578                 else
579               should_update = false
580               h.each_key { |key|
581                     val = value(key)
582                     if val && h[key] != val
583                       should_update = true
584                       break
585                     end
586                   }
587                   item_with_tag("update")[:enabled] = should_update
588                 end
589                 on_update_attr.call
590           }
591           on_mo_action = lambda { |it|
592             mo = it[:value]
593                 if mo_ao == 0
594                   if motype == "UHF"
595                     mo_index = (mo / 2) + (mo % 2 == 1 ? ncomps : 0)
596                         if mo_index < alpha || (mo_index >= ncomps && mo_index < ncomps + beta)
597                           occ_new = 1
598                         else
599                           occ_new = 0
600                         end
601                   else
602                     mo_index = mo
603                         if mo_index < alpha && mo_index < beta
604                           occ_new = 1
605                         elsif mo_index < alpha || mo_index < beta
606                           occ_new = -1
607                         else
608                           occ_new = 0
609                         end
610                   end
611                 else
612                   mo_index = mo
613       occ_new = occ
614       if mo_menu[mo]
615         if mo_menu[mo] =~ /(\(ryd)|(\(RY)|(\(NB)|(\*)/
616           occ_new = 0
617         else
618           occ_new = 1
619         end
620       end
621                 end
622                 coeffs = nil
623                 if occ_new != occ
624                   #  Set default color
625                   col = (occ_new == 0 ? 1 : (occ_new == -1 ? 2 : 0))
626                   set_value("color", col)
627                   h["color"] = col
628                   occ = occ_new
629                 end
630                 item_with_tag("table")[:refresh] = true
631             on_action.call(it)
632                 on_update_attr.call
633           }
634           on_set_action = lambda { |it|
635             if mo_ao != it[:value]
636         mo_ao = it[:value]
637         mo_menu = update_mo_labels.call(mo_ao)
638         it0 = item_with_tag("mo")
639         val = it0[:value]
640         it0[:subitems] = mo_menu
641         it0[:value] = val   #  Keep the mo number invariant
642         h["mo"] = nil  # "Update" button is forced to be enabled
643         coeffs = nil
644         coeffs_matrix = nil  #  Matrix needs update
645         on_mo_action.call(it0)
646       end
647       on_update_attr.call
648           }
649     on_basis_action = lambda { |it|
650       if h["basis"] != value("basis")
651         table_coeffs_matrix = nil
652         item_with_tag("table")[:refresh] = true
653       end
654     }
655           on_update = lambda { |it|
656             h.each_key { |key|
657                   h[key] = value(key)
658                 }
659                 opac = h["opacity"].to_f
660                 opac = 0.0 if opac < 0.0
661                 opac = 1.0 if opac > 1.0
662                 color = coltable[h["color"]] + [opac]
663                 color0 = [1, 1, 1, opac]
664                 thres = h["threshold"].to_f
665                 thres = 0.001 if thres >= 0.0 && thres < 0.001
666                 thres = -0.001 if thres <= 0.0 && thres > -0.001
667     if h["reverse"] == 1
668       thres = -thres
669     end
670                 expand = h["expand"].to_f
671                 expand = 0.01 if expand < 0.01
672                 expand = 10.0 if expand > 10.0
673                 grid = h["grid"].to_i
674                 if grid > 10000000
675                   grid = 10000000
676                 end
677                 if mo_ao == 0
678                   idx = mo_index + 1
679                 else
680                   idx = 0
681                   mol.set_mo_coefficients(0, 0.0, coeffs)
682                 end
683                 if it[:tag] == "create_cube"
684                   basename = File.basename(mol.path || mol.name, ".*")
685               fname1 = Dialog.save_panel("Cube file name", mol.dir || Dir.pwd, basename + ".cube", "Gaussian cube file (*.cube)|*.cube")
686                   if fname1
687                     mol.cubegen(fname1, idx, grid, true)
688                   end
689                 else
690                   mol.create_surface(idx, :npoints=>grid, :color=>color, :thres=>thres, :expand=>expand, :color0=>color0)
691                   mol.show_surface
692                   on_action.call(it)
693                   surface_dialog_attr["hidden"] = 0
694                   on_update_attr.call
695                 end
696           }
697           layout(1,
698             layout(2,
699                   item(:text, :title=>"Orbital Set"),
700                   item(:popup, :tag=>"mo_ao", :subitems=>mo_ao_items, :action=>on_set_action),
701               item(:text, :title=>"Select"),
702               item(:popup, :tag=>"mo", :subitems=>mo_menu, :action=>on_mo_action)),
703                 layout(4,
704                   item(:text, :title=>"Color"),
705                   item(:popup, :tag=>"color", :subitems=>["blue", "red", "green", "yellow", "cyan", "magenta", "black", "white"], :action=>on_action),
706                   item(:text, :title=>"Opacity"),
707                   item(:textfield, :tag=>"opacity", :width=>80, :value=>"0.8", :action=>on_action),
708                   item(:text, :title=>"Threshold"),
709                   item(:textfield, :tag=>"threshold", :width=>80, :value=>"0.05", :action=>on_action),
710           item(:checkbox, :tag=>"reverse", :title=>"Reverse Phase", :action=>on_action),
711           -1),
712                 layout(4,
713                   item(:text, :title=>"Number of Grid Points"),
714                   item(:textfield, :tag=>"grid", :width=>80, :value=>"512000", :action=>on_action),
715           item(:text, :title=>"Expand box"),
716           item(:textfield, :tag=>"expand", :width=>80, :value=>"1.0", :action=>on_action),
717           item(:checkbox, :tag=>"showbox", :title=>"Show box", :action=>on_action),
718           -1, -1, -1),
719     #  table coefficients are based on "basis" (AO, NAO, etc.)
720     #  MO is not allowed as basis, so the "basis" menu begins with "AO"
721     layout(2,
722            item(:text, :title=>"Coeffs based on:"),
723            item(:popup, :tag=>"basis", :subitems=>mo_ao_keys[1..-1], :value=>0, :action=>on_basis_action)),
724     item(:table, :width=>380, :height=>300, :tag=>"table",
725                   :columns=>[["Atom", 40], ["Name", 60], ["Label", 140], ["Coeff", 120]],
726                   :on_count=> lambda { |it| ncomps },
727                   :on_get_value=>on_get_value,
728                   :flex=>[0,0,0,0,1,1]),
729                 layout(3,
730                   item(:button, :tag=>"update", :title=>"Update", :action=>on_update),
731                   item(:button, :tag=>"clear", :title=>"Clear", :action=>lambda { |it|
732                     mol.clear_surface
733                         item_with_tag("update")[:enabled] = true
734                         surface_dialog_attr["hidden"] = -1
735                         on_update_attr.call
736                     } ),
737                   item(:button, :tag=>"hide", :title=>"Hide", :action=>lambda { |it|
738                     case surface_dialog_attr["hidden"]
739                         when 0
740                           surface_dialog_attr["hidden"] = 1
741                           mol.hide_surface
742                         when 1
743                           surface_dialog_attr["hidden"] = 0
744                           mol.show_surface
745                         end
746                         on_update_attr.call
747                         } ),
748                   item(:button, :tag=>"create_cube", :title=>"Create Cube", :action=>on_update),
749                   :flex=>[0,1,0,0,0,0]),
750                 :flex=>[0,0,0,0,1,1]
751           )
752           tags.each { |tag|
753             if (val = surface_dialog_attr[tag]) != nil
754                   item_with_tag(tag)[:value] = val
755                 end
756           }
757           mo_idx = surface_dialog_attr["mo"]
758           on_update_attr.call
759           on_set_action.call(item_with_tag("mo_ao"))
760           item_with_tag("mo")[:value] = surface_dialog_attr["mo"] = mo_idx
761           size = self.size
762           set_min_size(size[0], 250)
763           item_with_tag("table")[:refresh] = true
764           show
765     }
766   end
767
768   def ask_graphic_export_scale
769     scale = get_global_settings("global.export_graphic_scale")
770         scale = (scale ? scale.to_i - 1 : 3)
771         bg_color = get_global_settings("global.export_background_color")
772         bg_color = (bg_color ? bg_color.to_i + 1 : 0)
773     hash = Dialog.run("Set Export Properties") {
774           layout(2,
775                 item(:text, :title=>"Resolution:"),
776                 item(:popup, :subitems=>["Screen", "Screenx2", "Screenx3", "Screenx4", "Screenx5"], :tag=>"resolution", :value=>scale),
777                 item(:text, :title=>"Background color:"),
778                 item(:popup, :subitems=>["Same as Screen", "Transparent", "Black", "White"], :tag=>"bg_color", :value=>bg_color))
779         }
780         if hash[:status] == 0
781           set_global_settings("global.export_graphic_scale", (hash["resolution"] + 1).to_s)
782           set_global_settings("global.export_background_color", (hash["bg_color"] - 1).to_s)
783           return 0
784         else
785           return -1
786         end
787   end
788   
789   #  DEBUG
790   def cmd_test
791     $test_dialog = Dialog.new("Test") { item(:text, :title=>"test"); show }
792   end
793   
794 end
795
796 module Kernel
797   def ask_scratch_dir
798     sdir = get_global_settings("global.scratch_dir")
799         while 1
800       p = Dialog.open_panel("Please select scratch directory", sdir, nil, true)
801           if p
802             if p =~ / /
803                   error_message_box("Please avoid path containing a white space.\n" + p.sub(/ /, "<!> <!>"))
804                   sdir = p
805                   next
806                 else
807                   set_global_settings("global.scratch_dir", p)
808                   return p
809             end
810           else
811             return nil
812           end
813         end
814   end
815 end
816
817 register_menu("Assign residue...", :cmd_assign_residue, :non_empty)
818 register_menu("Offset residue...", :cmd_offset_residue, :non_empty)
819 register_menu("Sort by residue", :cmd_sort_by_residue, :non_empty)
820 register_menu("", "")
821 register_menu("Delete Frames...", :cmd_delete_frames, lambda { |m| m && m.nframes > 1 } )
822 register_menu("Reverse Frames...", :cmd_reverse_frames, lambda { |m| m && m.nframes > 1 } )
823 register_menu("Concatenate Frames...", :cmd_concat_frames, lambda { |m| m && m.nframes > 1 } )
824 register_menu("", "")
825 register_menu("Show Energy Window...", :cmd_show_energy, lambda { |m| m && m.property_names.include?("energy") } )
826 register_menu("Show MO Surface...", :cmd_create_surface, lambda { |m| m && m.get_mo_info(:type) != nil } )
827 #register_menu("cmd test", :cmd_test)
828