Scid  4.7.0
win.tcl
Go to the documentation of this file.
1 # Copyright (C) 2008-2009 Pascal Georges
2 # Copyright (C) 2013-2018 Fulvio Benini
3 #
4 # This file is part of Scid (Shane's Chess Information Database).
5 #
6 # Scid is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation.
9 #
10 # Scid is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Scid. If not, see <http://www.gnu.org/licenses/>.
17 
18 namespace eval ::win {}
19 
20 # Creates a docked/undocked window.
21 proc ::win::createWindow { {w} {title} {default_geometry ""} } {
22  if { [winfo exists $w] } {
23  return 0
24  }
25 
26  # Set default width and height values, if they do not exists
27  if {![info exists ::winGeometry($w)]} {
28  set ::winGeometry($w) $default_geometry
29  }
30 
31  # Create the window
32  frame $w
33  ::win::manageWindow $w $title
34 
36 
37  return 1
38 }
39 
40 # Close a window, independently of its docked state.
41 # If the window is undocked the window geometry is saved.
42 proc ::win::closeWindow {w} {
43  lassign [::win::isDocked $w] docked_nb w
44  if {$docked_nb ne ""} {
45  ::docking::remove_tab $w $docked_nb
46  } else {
48  }
49  destroy $w
50 }
51 
52 # Returns a list containing the names of the opened windows:
53 proc ::win::getWindows {} {
54  set res {}
55  foreach undocked [array names ::docking::notebook_name] {
56  if {[winfo exists $undocked]} {
57  lappend res $undocked
58  }
59  }
60  foreach noteb [array names ::docking::tbs] {
61  foreach docked [$noteb tabs] {
62  lappend res $docked
63  }
64  }
65  return $res
66 }
67 
68 # if undocked window : sets the title of the toplevel window.
69 # if docked : sets the name of the tab.
70 # TODO: ::win::setTitle
71 proc setTitle { w title } {
72  lassign [::win::isDocked $w] docked_nb w
73  if {$docked_nb ne ""} {
74  # in docked mode trim down title to spare space
75  if {[string equal -length 6 $title "Scid: "]} {
76  set title [string range $title 6 end]
77  }
78  $docked_nb tab $w -text $title
79  } else {
80  wm title $w $title
81  }
82 }
83 
84 # Return a list containing the name of the menu (or "" if a menu do not exists)
85 # and the unadultered window's name (no fdock)
86 # param w: the window's name (with or without fdock)
87 proc ::win::getMenu {w} {
88  lassign [::win::isDocked $w] docked_nb wnd
89  if {[string equal -length 6 $wnd ".fdock"]} {
90  set w [string replace $wnd 1 5]
91  }
92  if {[info exists ::win::menu_($wnd)]} {
93  return [list $::win::menu_($wnd) $w]
94  }
95  return [list "" $w]
96 }
97 
98 # if undocked window : sets the menu of the toplevel window.
99 # if docked : displays a menu icon in the tab.
100 # param w: the window's name (without fdock)
101 # TODO: ::win::setMenu
102 proc setMenu {w m} {
103  lassign [::win::isDocked $w] docked_nb wnd
104  if {$docked_nb ne ""} {
105  $docked_nb tab $wnd -image tb_tabmenu -compound left
106  } else {
107  $w configure -menu $m
108  }
109  set ::win::menu_($wnd) $m
110 
111  ::appearance::applyMenuBarColor $m $docked_nb
112 }
113 
114 # Save the geometry of an undocked toplevel window.
115 proc ::win::saveWinGeometry {w} {
116  lassign [::win::isDocked $w] docked_nb w
117  if {$docked_nb eq ""} {
118  update idletasks
119  if {[wm state $w] == "zoomed"} {
120  set ::winGeometry($w) "zoomed"
121  } else {
122  set ::winGeometry($w) [wm geometry $w]
123  }
124  }
125 }
126 
127 # Restore the geometry of an undocked toplevel window.
128 # return true if a stored geometry was available.
129 proc ::win::restoreWinGeometry {w} {
130  if {[info exists ::winGeometry($w)]} {
131  if {$::winGeometry($w) == "zoomed"} {
132  if { $::windowsOS || $::macOS } {
133  wm state $w zoomed
134  } else {
135  wm attributes $w -zoomed
136  }
137  } else {
138  wm geometry $w $::winGeometry($w)
139  }
140  return 1
141  }
142  return 0
143 }
144 
145 # Return a list containing:
146 # - the name of the notebook containing the window ("" if undocked)
147 # - the name of the top parent window
148 proc ::win::isDocked {wnd} {
149  # Get the window at the top of the hierarchy (not the toplevel)
150  regexp {[.]\w*} "$wnd" wnd
151  set f ".fdock[string range $wnd 1 end]"
152  if {[winfo exists $f]} { set wnd $f}
153 
154  set docked_nb [ ::docking::find_tbn $wnd]
155  return [list $docked_nb $wnd]
156 }
157 
158 # Undock a toplevel window
159 proc ::win::undockWindow { wnd srctab {title ""} } {
160  if {$srctab ne "" } {
161  set old_options [::docking::remove_tab $wnd $srctab]
162  set title "Scid: [dict get $old_options -text]"
163  }
164 
165  wm manage $wnd
166  wm title $wnd $title
167  wm protocol $wnd WM_DELETE_WINDOW "::win::closeWindow $wnd"
168 
169  lassign [::win::getMenu $wnd] menu wmenu
170  if {$menu ne ""} {
171  #HACK: In Linux (tk8.6.8) without "update idletasks"
172  # sometimes the menu is not shown.
173  update idletasks
174  ::setMenu $wmenu $menu
175  }
176 
177  # Remember the source notebook
178  set ::docking::notebook_name($wnd) $srctab
179 
180  #HACK: In Linux (tk8.6.8) without "after idle after 1"
181  # sometimes the geometry is not restored correctly.
182  after idle after 1 "::win::restoreWinGeometry $wnd"
183 }
184 
185 # Dock a toplevel window
186 proc ::win::dockWindow {wnd} {
188  # in docked mode trim down title to spare space
189  set title [wm title $wnd]
190  if {[string equal -length 6 $title "Scid: "]} {
191  set title [string range $title 6 end]
192  }
193 
194  lassign [::win::getMenu $wnd] menu wmenu
195  $wmenu configure -menu {}
196 
197  wm forget $wnd
198 
199  if {[winfo exists $::docking::notebook_name($wnd)]} {
200  set dsttab $::docking::notebook_name($wnd)
201  } else {
202  set dsttab [::docking::choose_notebook $wnd]
203  }
204  unset ::docking::notebook_name($wnd)
205  ::docking::insert_tab $wnd $dsttab end \
206  [list -text $title -image tb_close -compound left]
207 
208  if {$menu ne ""} { ::setMenu $wmenu $menu}
209 }
210 
211 # Toggle the docked/undocked status of a window
212 # param wnd: the (child) widget
213 proc ::win::toggleDocked {wnd} {
214  lassign [::win::isDocked $wnd] docked_nb wnd
215 
216  # Check if the window can be docked/undocked
217  if {$wnd eq ".fdockmain" || [winfo class $wnd] ne "Frame"} {
218  return
219  }
220 
221  if {$docked_nb ne ""} {
222  ::win::undockWindow $wnd $docked_nb
223  } else {
224  ::win::dockWindow $wnd
225  }
226 }
227 
228 proc ::win::manageWindow {wnd title} {
229  unset -nocomplain ::win::menu_($wnd)
230  unset -nocomplain ::docking::notebook_name($wnd)
231 
232  if { [info exists ::docking::layout_dest_notebook]} {
233  set dsttab $::docking::layout_dest_notebook
234  set docked [expr { $dsttab ne "undocked" }]
235  } else {
236  set docked $::windowsDock
237  }
238  if {$docked} {
239  if {![info exists dsttab]} {
240  set dsttab [::docking::choose_notebook $wnd]
241  }
242  ::docking::insert_tab $wnd $dsttab end \
243  [list -text $title -image tb_close -compound left]
244  } else {
245  ::win::undockWindow $wnd "" $title
246  }
247 }
248 
249 # createDialog
250 # Standard initialize a toplevel window with unique attributes
251 # y is used for windows with menu. They do not need a border on the top and call with y=0
252 proc ::win::createDialog {w {y 10}} {
253  toplevel $w -padx 10 -pady $y
255 }
256 
257 # Make sure that a window is visible
258 proc ::win::makeVisible { wnd } {
259  lassign [::win::isDocked $wnd] wnd_nb wnd_top
260  if {$wnd_nb ne ""} {
261  $wnd_nb select $wnd_top
262  set wnd_top [winfo toplevel $wnd_top]
263  }
264  ::raise $wnd_top
265  wm deiconify $wnd_top
266 }
267 
268 # ::utils::win::Centre
269 #
270 # Centres a window on the screen.
271 #
272 proc ::utils::win::Centre {w} {
273  wm withdraw $w
274  update idletasks
275  set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
276  - [winfo vrootx .]}]
277  set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
278  - [winfo vrooty .]}]
279  wm geom $w +$x+$y
280  wm deiconify $w
281 }
282 
283 ################################################################################
284 #
285 # DockingFramework
286 #
287 # Code is inspired by
288 # http://wiki.tcl.tk/21846
289 # which is published under BSD license
290 #
291 ################################################################################
292 
293 namespace eval docking {
294  # associates notebook to paned window
295  variable tbs
296 }
297 
298 ################################################################################
299 # find notebook, corresponding to path
300 proc ::docking::find_tbn {path} {
301  foreach tb [array names ::docking::tbs] {
302  if {[lsearch -exact [$tb tabs] $path]>=0} {
303  return $tb
304  }
305  }
306  return {}
307 }
308 
309 ################################################################################
310 # always keep .pw paned window
311 proc ::docking::_cleanup_tabs {srctab} {
312  variable tbs
313 
314  # if srctab is empty, then remove it
315  if {[llength [$srctab tabs]]==0} {
316  destroy $srctab
317  set pw $tbs($srctab)
318  unset tbs($srctab)
319 
320  while {[llength [$pw panes]]==0} {
321  set parent [winfo parent $pw]
322 
323  if {$pw == ".pw"} {
324  break
325  }
326  destroy $pw
327  set pw $parent
328  }
329 
330  }
331 }
332 ################################################################################
333 ################################################################################
334 ################################################################################
335 
336 
337 # The coefficients for the selections of the container Notebook
338 # have been calculated doing a linear regression of this matrix:
339 # board tabs tabs^2 similar sim^2 sim^3 area fitness
340 # 0 0 0 0 0 0 0,9 120900
341 # 0 0 0 0 0 0 0,5 120500
342 # 0 0 0 0 0 0 0,1 120100
343 # 0 5 25 5 25 125 0,9 99900
344 # 0 5 25 5 25 125 0,5 99500
345 # 0 5 25 5 25 125 0,1 99100
346 # 0 3 9 3 9 27 0,9 93900
347 # 0 3 9 3 9 27 0,5 93500
348 # 0 3 9 3 9 27 0,1 93100
349 # 0 2 4 2 4 8 0,9 87900
350 # 0 2 4 2 4 8 0,5 87500
351 # 0 2 4 2 4 8 0,1 87100
352 # 0 4 16 3 9 27 0,9 81900
353 # 0 4 16 3 9 27 0,5 81500
354 # 0 4 16 3 9 27 0,1 81100
355 # 0 3 9 2 4 8 0,9 75900
356 # 0 3 9 2 4 8 0,5 75500
357 # 0 3 9 2 4 8 0,1 75100
358 # 0 2 4 1 1 1 0,9 69900
359 # 0 2 4 1 1 1 0,5 69500
360 # 0 2 4 1 1 1 0,1 69100
361 # 0 3 9 1 1 1 0,9 63900
362 # 0 3 9 1 1 1 0,5 63500
363 # 0 3 9 1 1 1 0,1 63100
364 # 0 2 4 1 1 1 0,9 57900
365 # 0 2 4 1 1 1 0,5 57500
366 # 0 2 4 1 1 1 0,1 57100
367 # 0 1 1 0 0 0 0,9 39900
368 # 0 1 1 0 0 0 0,5 39500
369 # 0 1 1 0 0 0 0,1 39100
370 # 0 3 9 0 0 0 0,9 33900
371 # 0 3 9 0 0 0 0,5 33500
372 # 0 3 9 0 0 0 0,1 33100
373 # 1 2 4 1 1 1 0,9 9900
374 # 1 2 4 1 1 1 0,5 9500
375 # 1 2 4 1 1 1 0,1 9100
376 # 1 1 1 0 0 0 0,9 7900
377 # 1 1 1 0 0 0 0,5 7500
378 # 1 1 1 0 0 0 0,1 7100
379 # 1 2 4 0 0 0 0,9 5900
380 # 1 2 4 0 0 0 0,5 5500
381 # 1 2 4 0 0 0 0,1 5100
382 # Improving the matrix and recalculating can improve the select algorithm
383 proc ::docking::choose_notebook { path } {
384  set dsttab {}
385  set best_fitting ""
386  foreach tb [array names ::docking::tbs] {
387  if {[winfo class $tb] != "TNotebook"} { continue}
388 
389  set tabs [$tb tabs]
390 
391  # Features
392  set feat(0) 1
393  set coeff(0) "105622.84"
394  # number of boards
395  set feat(1) [llength [lsearch -all -regexp $tabs ".*main"]]
396  set coeff(1) "-48019.31"
397  # number of tabs
398  set feat(2) [llength $tabs]
399  set coeff(2) "-51266.84"
400  # number of tabs^2
401  set feat(3) [expr { $feat(2) * $feat(2) }]
402  set coeff(3) "8661.97"
403  # number of similar windows
404  set name_striptrailnum [regsub {\d*$} $path ""]
405  set feat(4) [llength [lsearch -all -regexp $tabs ".*$name_striptrailnum.*"]]
406  set coeff(4) "29942.45"
407  # number of similar windows^2
408  set feat(5) [expr { $feat(4) * $feat(4) }]
409  set coeff(5) "-3053.05"
410  # number of similar windows^3
411  set feat(6) [expr { $feat(4) * $feat(4) * $feat(4) }]
412  set coeff(6) "-323.52"
413  # ratio between the area of the notebook and the screen
414  set feat(7) [expr { double([winfo width $tb] * [winfo height $tb]) }]
415  set feat(7) [expr { $feat(7) / ([winfo screenwidth $tb] * [winfo screenheight $tb]) }]
416  set coeff(7) "1000"
417 
418  set fit 0;
419  for {set i 0} {$i < [array size feat]} {incr i} {
420  set fit [expr { $fit + $feat($i) * $coeff($i)}]
421  }
422 
423  if {$best_fitting == "" || $fit > $best_fitting} {
424  set best_fitting $fit
425  set dsttab $tb
426  }
427  }
428  return $dsttab
429 }
430 
431 # Insert a window into a notebook
432 proc ::docking::insert_tab {wnd dest_noteb {dest_pos "end"} {options ""}} {
433  $dest_noteb insert $dest_pos $wnd {*}$options
434  $dest_noteb select $wnd
435  raise $wnd
436 }
437 
438 # Remove a window from a notebook
439 proc ::docking::remove_tab {wnd src_noteb} {
440  set options [$src_noteb tab $wnd]
441  $src_noteb forget $wnd
442  ::docking::_cleanup_tabs $src_noteb
443  return $options
444 }
445 
446 proc ::docking::generate_unique_path_ { prefix } {
447  set tmp 0
448  while {[winfo exists $prefix$tmp]} {
449  incr tmp
450  }
451  return "$prefix$tmp"
452 }
453 
454 # Move a window between two different notebooks
455 proc ::docking::move_tab_ {wnd src_noteb dest_noteb {dest_pos "end"} } {
456  set options [::docking::remove_tab $wnd $src_noteb]
457  if {[string length $dest_noteb] == 1} {
458  set idx [::docking::orient_pw_ $src_noteb $dest_noteb]
459  if {$dest_noteb eq "s" || $dest_noteb eq "e"} {
460  incr idx
461  }
462  set pw $::docking::tbs($src_noteb)
463  set dest_noteb [::docking::generate_unique_path_ $pw.tb]
464  ::docking::create_notebook_ $dest_noteb
465  ::docking::insert_notebook_ $pw $idx $dest_noteb
466  }
467  ::docking::insert_tab $wnd $dest_noteb $dest_pos $options
468 }
469 
470 # Given a notebook, orient its paned window so that a new notebook can be added
471 # in the wanted direction. Return the idx of the notebook.
472 proc ::docking::orient_pw_ {tbn anchor} {
473  variable tbs
474 
475  if {$anchor=="w" || $anchor=="e"} {
476  set orient "horizontal"
477  } else {
478  set orient "vertical"
479  }
480 
481  set pw $tbs($tbn)
482  set idx [lsearch -exact [$pw panes] $tbn]
483 
484  if {[$pw cget -orient] ne $orient} {
485  # create new paned window
486  set old_pw $pw
487  set pw [::docking::generate_unique_path_ $pw.pw]
488  ttk::panedwindow $pw -orient $orient
489  # move old notebook
490  $old_pw forget $tbn
491  ::docking::insert_notebook_ $pw end $tbn
492  ::docking::insert_pane_ $old_pw $idx $pw
493  set idx 0
494  }
495  return $idx
496 }
497 
498 # Insert a new pane into a paned window
499 proc ::docking::insert_pane_ {pw idx wnd} {
500  if {$idx ne "end" && $idx >= [llength [$pw panes]]} {
501  set idx "end"
502  }
503  $pw insert $idx $wnd -weight 1
504 }
505 
506 # Insert a notebook into a paned window
507 proc ::docking::insert_notebook_ {pw idx noteb} {
508  ::docking::insert_pane_ $pw $idx $noteb
509  set ::docking::tbs($noteb) $pw
510 }
511 
512 # Create a new notebook
513 proc ::docking::create_notebook_ {path} {
514  set noteb [ttk::notebook $path -width 1 -height 1]
515  bind $noteb <B1-Motion> {
516  if {[info exists ::docking::motion_]} { continue }
517  set ::docking::motion_ [::docking::identify_tab_ %W %x %y]
518  if {[lindex $::docking::motion_ 0] eq ""} {
519  # Do nothing if motion started outside a tab
520  continue
521  }
522  if {[lindex $::docking::motion_ 1]} {
523  # Not a motion event if it starts and ends over the icon
524  unset ::docking::motion_
525  } else {
526  %W configure -cursor hand2
527  }
528  }
529  bind $noteb <ButtonRelease-1> {
530  if {[info exists ::docking::motion_]} {
531  %W configure -cursor {}
532  ::docking::manage_motion_ %W %X %Y
533  } else {
534  ::docking::manage_click_ %W %X %Y %x %y
535  }
536  }
537  bind $noteb <ButtonRelease-$::MB3> {
538  ::docking::manage_rightclick_ %W %X %Y %x %y
539  }
540  return $noteb
541 }
542 
543 # Given the x y coords relative to a notebook, returns a list containing the
544 # index of the tab (or "") and true if the point given is over the tab's image.
545 proc ::docking::identify_tab_ {noteb localX localY} {
546  set isIcon 0
547  set tab [$noteb identify tab $localX $localY]
548  if {$tab ne ""} {
549  set icon [$noteb tab $tab -image]
550  if {$icon ne ""} {
551  set iconW [expr { 4 + [image width $icon] }]
552  if {$tab == 0} {
553  set isIcon [expr {$localX < $iconW ? 1 : 0}]
554  } else {
555  set isIcon [expr [$noteb identify tab [expr $localX - $iconW] $localY] != $tab]
556  }
557  }
558  }
559  return [list $tab $isIcon]
560 }
561 
562 # Relocate tabs in response to drag events.
563 proc ::docking::manage_motion_ {src_noteb x y} {
564  lassign $::docking::motion_ src_tab
565  unset ::docking::motion_
566  if {$src_tab eq ""} { return}
567 
568  set dest_noteb [winfo containing $x $y]
569  if {![info exists ::docking::tbs($dest_noteb)]} { return}
570 
571  set localX [expr $x-[winfo rootx $dest_noteb]]
572  set localY [expr $y-[winfo rooty $dest_noteb]]
573  set dest_pos [$dest_noteb identify tab $localX $localY]
574  if {$dest_pos eq ""} { set dest_pos "end"}
575 
576  set wnd [lindex [$src_noteb tabs] $src_tab]
577  if {$src_noteb eq $dest_noteb} {
578  $dest_noteb insert $dest_pos $wnd
579  } else {
580  ::docking::move_tab_ $wnd $src_noteb $dest_noteb $dest_pos
581  }
582 }
583 
584 # Special handling of a left click on a tab's icon: show an associated menu
585 # if it exists, otherwise close the window.
586 proc ::docking::manage_click_ {noteb x y localX localY} {
587  lassign [::docking::identify_tab_ $noteb $localX $localY] tab isIcon
588  if {$tab eq "" || ! $isIcon} { return}
589 
590  set wnd [lindex [$noteb tabs] $tab]
591  lassign [::win::getMenu $wnd] menu
592  if {$menu ne ""} {
593  tk_popup $menu $x $y
594  } else {
595  ::win::closeWindow $wnd
596  }
597 }
598 
599 # Right click on a tab label: show a windows management menu.
600 proc ::docking::manage_rightclick_ {noteb x y localX localY} {
601  lassign [::docking::identify_tab_ $noteb $localX $localY] tab isIcon
602  if {$tab eq "" || $isIcon} { return}
603 
604  $noteb select $tab
605 
606  set noteb_tabs [$noteb tabs]
607  set state [expr { [llength $noteb_tabs] > 1 ? "normal" : "disabled" }]
608  set wnd [lindex $noteb_tabs $tab]
609 
610  set m .ctxtMenu
611  if { [winfo exists $m] } { destroy $m}
612  menu $m -tearoff 0
613  $m add command -label [ ::tr DockTop] -state $state \
614  -command "::docking::move_tab_ $wnd $noteb n"
615  $m add command -label [ ::tr DockBottom] -state $state \
616  -command "::docking::move_tab_ $wnd $noteb s"
617  $m add command -label [ ::tr DockLeft] -state $state \
618  -command "::docking::move_tab_ $wnd $noteb w"
619  $m add command -label [ ::tr DockRight] -state $state \
620  -command "::docking::move_tab_ $wnd $noteb e"
621  # Main board can not be closed or undocked
622  if { $wnd != ".fdockmain" } {
623  $m add separator
624  $m add command -label [ ::tr Undock] -command "::win::undockWindow $wnd $noteb"
625  $m add command -label [ ::tr Close] -command "::win::closeWindow $wnd"
626  }
627  tk_popup $m $x $y
628 }
629 
630 
631 ################################################################################
632 # Layout management
633 ################################################################################
634 
635 ################################################################################
636 # saves layout (bail out if some windows cannot be restored like FICS)
637 proc ::docking::layout_save { slot } {
638  #TODo: Save FICS window
639 
640  # on Windows the geometry is false if the window was maximized (x and y offsets are the ones before the maximization)
641  set geometry [wm geometry .]
642  set ::docking::layout_list($slot) [list [list "MainWindowGeometry" $geometry]]
643  if {[wm state .] == "zoomed"} {
644  if { [scan $geometry "%dx%d+%d+%d" w h x y] == 4 } {
645  set geometry "${w}x${h}+0+0"
646  set ::docking::layout_list($slot) [list [list "MainWindowGeometry" $geometry "zoomed"]]
647  }
648  }
649 
650  lappend ::docking::layout_list($slot) [ layout_save_pw .pw]
651 
652  # Append undocked windows
653  foreach wnd [array names ::docking::notebook_name] {
654  if {[winfo exists $wnd]} {
655  lappend ::docking::layout_list($slot) [list [list "Toplevel" $wnd]]
656  }
657  }
658 }
659 ################################################################################
660 proc ::docking::layout_save_pw {pw} {
661  set ret {}
662 
663  # record sash position for each panes
664  set sashpos {}
665  for {set i 0} {$i < [ expr [llength [$pw panes]] -1]} {incr i} {
666  lappend sashpos [$pw sashpos $i]
667  }
668  lappend ret [list $pw [$pw cget -orient] $sashpos]
669 
670  foreach p [$pw panes] {
671  if {[winfo class $p] == "TNotebook"} {
672  set wins [$p tabs]
673  # Keep only the first glistWin in each pane
674  set glistWins [lsearch -all -regexp $wins "\.(fdock)?glistWin\[0-9\]+"]
675  set i [llength $glistWins]
676  while {$i > 1} {
677  incr i -1
678  set remove [lindex $glistWins $i]
679  set wins [lreplace $wins $remove $remove]
680  }
681  lappend ret [list "TNotebook" $p $wins]
682 
683  } elseif {[winfo class $p] == "TPanedwindow"} {
684  lappend ret [ list "TPanedwindow" [layout_save_pw $p]]
685  }
686  }
687 
688  return $ret
689 }
690 
691 ################################################################################
692 # restores paned windows and internal notebooks
693 proc ::docking::layout_restore_pw { data } {
694  foreach elt $data {
695  lassign $elt type pathName
696  if {$type == "Toplevel"} {
697  lappend ::docking::restore_wnds [list "undocked" $pathName]
698 
699  } elseif {$type == "TPanedwindow"} {
700  layout_restore_pw [lindex $elt 1]
701 
702  } elseif {$type == "TNotebook"} {
704  ::docking::insert_notebook_ $pw end $pathName
705  foreach wnd [lindex $elt 2] {
706  lappend ::docking::restore_wnds [list $pathName $wnd]
707  }
708 
709  } else {
710  lassign $elt pw orient sash_positions
711  if {$sash_positions ne ""} {
712  lappend ::docking::restore_sashpos [ list $pw $sash_positions]
713  }
714  # build a new pw
715  ttk::panedwindow $pw -orient $orient
716  set parent [string range $pw 0 [expr [string last "." $pw]-1]]
717  if { $parent eq "" } {
718  pack $pw -fill both -expand true
719  } else {
720  ::docking::insert_pane_ $parent end $pw
721  }
722  }
723  }
724 }
725 
726 proc ::docking::create_window {wnd} {
727  switch -regexp -matchvar regmatch -- $wnd {
728  "\.(fdock)?main" { ::docking::insert_tab $wnd $::docking::layout_dest_notebook end [list -text $::tr(Board)]}
729  "\.(fdock)?pgnWin" { ::pgn::OpenClose
730  ::pgn::Refresh 1}
731  "\.(fdock)?baseWin" { ::windows::switcher::Open}
732  "\.(fdock)?bookWin" { ::book::open}
733  "\.(fdock)?ecograph" { ::windows::eco::OpenClose}
734  "\.(fdock)?tbWin" { ::tb::Open}
735  "\.(fdock)?commentWin" { ::makeCommentWin}
736  "\.(fdock)?ccWindow" { ::CorrespondenceChess::CCWindow}
737  "\.(fdock)?oprepWin" { ::optable::makeReportWin}
738  "\.(fdock)?plist" { ::plist::Open}
739  "\.(fdock)?tourney" { ::tourney::Open}
740  "\.(fdock)?sgraph" { ::tools::graphs::score::Refresh}
741  "\.(fdock)?glistWin([0-9]+)" { ::windows::gamelist::Open}
742  "\.(fdock)?treeWin([0-9]+)" { ::tree::make [lindex $regmatch end]}
743  "\.(fdock)?analysisWin([0-9]+)" { ::makeAnalysisWin [lindex $regmatch end] 0 0}
744  "\.(fdock)?crosstableWin" { ::crosstab::Open}
745  }
746 }
747 
748 proc ::docking::layout_restore { slot } {
749  # if no layout recorded, retry with the last used
750  if { $::docking::layout_list($slot) == {} } {
751  if { $slot != "auto" } { ::docking::layout_restore "auto"}
752  return
753  }
754 
755  # closeAll
756  foreach wnd [::win::getWindows] {
757  ::win::closeWindow $wnd
758  }
759  foreach wnd [winfo children .] {
760  if { [winfo class $wnd] ne "Menu" } {
761  destroy $wnd
762  }
763  }
764 
765  # Parse geometry, sashpos and windows; create paned windows and notebooks
766  set restore_geometry {}
767  set ::docking::restore_sashpos {}
768  set ::docking::restore_wnds {}
769  foreach data $::docking::layout_list($slot) {
770  if {[lindex $data 0] eq "MainWindowGeometry"} {
771  lappend restore_geometry [list "." [lindex $data 1] [lindex $data 2]]
772  } else {
774  }
775  }
776 
777  # Restore geometry
778  foreach geom $restore_geometry {
779  lassign $geom wnd size_pos zoomed
780  wm geometry $wnd $size_pos
781  if {$zoomed eq "zoomed"} {
782  if { $::windowsOS || $::macOS } {
783  wm state $wnd zoomed
784  } else {
785  wm attributes $wnd -zoomed
786  }
787  }
788  }
789 
790  # Restore paned windows' sash positions
791  foreach sashpos $::docking::restore_sashpos {
792  # It is necessary to process all the events beforehand because
793  # "sashpos" will query the panedwindow's available space.
794  update
795 
796  lassign $sashpos pw sash
797  set i 0
798  foreach pos $sash {
799  $pw sashpos $i $pos
800  incr i
801  }
802  }
803  unset -nocomplain ::docking::restore_sashpos
804 
805  # Create .main beforehand because some other windows depend on it
806  # TODO: remove the dependencies
807  set ::docking::layout_dest_notebook [::docking::choose_notebook $wnd]
808  ::CreateMainBoard .main
809  lassign [::win::isDocked .main] docked_nb w
810  $docked_nb forget $w
811 
812  # Restore windows
813  foreach pair $::docking::restore_wnds {
814  lassign $pair ::docking::layout_dest_notebook wnd
816 
817  # Needed for ttk::notebooks with multiple tabs (e.g., .baseWin and. main)
818  # that are not displayed correctly otherwise (due to "notebook select").
819  update
820  }
821  unset -nocomplain ::docking::layout_dest_notebook
822  unset -nocomplain ::docking::restore_wnds
823 
824  ::win::makeVisible .main.board
825  ::focus .main.board
826 }