bgerror.tcl 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. # bgerror.tcl --
  2. #
  3. # Implementation of the bgerror procedure. It posts a dialog box with
  4. # the error message and gives the user a chance to see a more detailed
  5. # stack trace, and possible do something more interesting with that
  6. # trace (like save it to a log). This is adapted from work done by
  7. # Donal K. Fellows.
  8. #
  9. # Copyright (c) 1998-2000 by Ajuba Solutions.
  10. # Copyright (c) 2007 by ActiveState Software Inc.
  11. # Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net>
  12. # Copyright (c) 2009 Pat Thoyts <patthoyts@users.sourceforge.net>
  13. namespace eval ::tk::dialog::error {
  14. namespace import -force ::tk::msgcat::*
  15. namespace export bgerror
  16. option add *ErrorDialog.function.text [mc "Save To Log"] \
  17. widgetDefault
  18. option add *ErrorDialog.function.command [namespace code SaveToLog]
  19. option add *ErrorDialog*Label.font TkCaptionFont widgetDefault
  20. if {[tk windowingsystem] eq "aqua"} {
  21. option add *ErrorDialog*background systemAlertBackgroundActive \
  22. widgetDefault
  23. option add *ErrorDialog*info.text.background white widgetDefault
  24. option add *ErrorDialog*Button.highlightBackground \
  25. systemAlertBackgroundActive widgetDefault
  26. }
  27. }
  28. proc ::tk::dialog::error::Return {which code} {
  29. variable button
  30. .bgerrorDialog.$which state {active selected focus}
  31. update idletasks
  32. after 100
  33. set button $code
  34. }
  35. proc ::tk::dialog::error::Details {} {
  36. set w .bgerrorDialog
  37. set caption [option get $w.function text {}]
  38. set command [option get $w.function command {}]
  39. if { ($caption eq "") || ($command eq "") } {
  40. grid forget $w.function
  41. }
  42. lappend command [$w.top.info.text get 1.0 end-1c]
  43. $w.function configure -text $caption -command $command
  44. grid $w.top.info - -sticky nsew -padx 3m -pady 3m
  45. }
  46. proc ::tk::dialog::error::SaveToLog {text} {
  47. if { $::tcl_platform(platform) eq "windows" } {
  48. set allFiles *.*
  49. } else {
  50. set allFiles *
  51. }
  52. set types [list \
  53. [list [mc "Log Files"] .log] \
  54. [list [mc "Text Files"] .txt] \
  55. [list [mc "All Files"] $allFiles] \
  56. ]
  57. set filename [tk_getSaveFile -title [mc "Select Log File"] \
  58. -filetypes $types -defaultextension .log -parent .bgerrorDialog]
  59. if {$filename ne {}} {
  60. set f [open $filename w]
  61. puts -nonewline $f $text
  62. close $f
  63. }
  64. return
  65. }
  66. proc ::tk::dialog::error::Destroy {w} {
  67. if {$w eq ".bgerrorDialog"} {
  68. variable button
  69. set button -1
  70. }
  71. }
  72. proc ::tk::dialog::error::DeleteByProtocol {} {
  73. variable button
  74. set button 1
  75. }
  76. proc ::tk::dialog::error::ReturnInDetails w {
  77. bind $w <Return> {}; # Remove this binding
  78. $w invoke
  79. return -code break
  80. }
  81. # ::tk::dialog::error::bgerror --
  82. #
  83. # This is the default version of bgerror.
  84. # It tries to execute tkerror, if that fails it posts a dialog box
  85. # containing the error message and gives the user a chance to ask
  86. # to see a stack trace.
  87. #
  88. # Arguments:
  89. # err - The error message.
  90. #
  91. proc ::tk::dialog::error::bgerror err {
  92. global errorInfo
  93. variable button
  94. set info $errorInfo
  95. set ret [catch {::tkerror $err} msg];
  96. if {$ret != 1} {return -code $ret $msg}
  97. # Ok the application's tkerror either failed or was not found
  98. # we use the default dialog then :
  99. set windowingsystem [tk windowingsystem]
  100. if {$windowingsystem eq "aqua"} {
  101. set ok [mc Ok]
  102. } else {
  103. set ok [mc OK]
  104. }
  105. # Truncate the message if it is too wide (>maxLine characters) or
  106. # too tall (>4 lines). Truncation occurs at the first point at
  107. # which one of those conditions is met.
  108. set displayedErr ""
  109. set lines 0
  110. set maxLine 45
  111. foreach line [split $err \n] {
  112. if { [string length $line] > $maxLine } {
  113. append displayedErr "[string range $line 0 [expr {$maxLine-3}]]..."
  114. break
  115. }
  116. if { $lines > 4 } {
  117. append displayedErr "..."
  118. break
  119. } else {
  120. append displayedErr "${line}\n"
  121. }
  122. incr lines
  123. }
  124. set title [mc "Application Error"]
  125. set text [mc "Error: %1\$s" $displayedErr]
  126. set buttons [list ok $ok dismiss [mc "Skip Messages"] \
  127. function [mc "Details >>"]]
  128. # 1. Create the top-level window and divide it into top
  129. # and bottom parts.
  130. set dlg .bgerrorDialog
  131. set bg [ttk::style lookup . -background]
  132. destroy $dlg
  133. toplevel $dlg -class ErrorDialog -background $bg
  134. wm withdraw $dlg
  135. wm title $dlg $title
  136. wm iconname $dlg ErrorDialog
  137. wm protocol $dlg WM_DELETE_WINDOW [namespace code DeleteByProtocol]
  138. if {$windowingsystem eq "aqua"} {
  139. ::tk::unsupported::MacWindowStyle style $dlg moveableAlert {}
  140. } elseif {$windowingsystem eq "x11"} {
  141. wm attributes $dlg -type dialog
  142. }
  143. ttk::frame $dlg.bot
  144. ttk::frame $dlg.top
  145. pack $dlg.bot -side bottom -fill both
  146. pack $dlg.top -side top -fill both -expand 1
  147. set W [ttk::frame $dlg.top.info]
  148. text $W.text -setgrid true -height 10 -wrap char \
  149. -yscrollcommand [list $W.scroll set]
  150. if {$windowingsystem ne "aqua"} {
  151. $W.text configure -width 40
  152. }
  153. ttk::scrollbar $W.scroll -command [list $W.text yview]
  154. pack $W.scroll -side right -fill y
  155. pack $W.text -side left -expand yes -fill both
  156. $W.text insert 0.0 "$err\n$info"
  157. $W.text mark set insert 0.0
  158. bind $W.text <ButtonPress-1> { focus %W }
  159. $W.text configure -state disabled
  160. # 2. Fill the top part with bitmap and message
  161. # Max-width of message is the width of the screen...
  162. set wrapwidth [winfo screenwidth $dlg]
  163. # ...minus the width of the icon, padding and a fudge factor for
  164. # the window manager decorations and aesthetics.
  165. set wrapwidth [expr {$wrapwidth-60-[winfo pixels $dlg 9m]}]
  166. ttk::label $dlg.msg -justify left -text $text -wraplength $wrapwidth
  167. ttk::label $dlg.bitmap -image ::tk::icons::error
  168. grid $dlg.bitmap $dlg.msg -in $dlg.top -row 0 -padx 3m -pady 3m
  169. grid configure $dlg.bitmap -sticky ne
  170. grid configure $dlg.msg -sticky nsw -padx {0 3m}
  171. grid rowconfigure $dlg.top 1 -weight 1
  172. grid columnconfigure $dlg.top 1 -weight 1
  173. # 3. Create a row of buttons at the bottom of the dialog.
  174. set i 0
  175. foreach {name caption} $buttons {
  176. ttk::button $dlg.$name -text $caption -default normal \
  177. -command [namespace code [list set button $i]]
  178. grid $dlg.$name -in $dlg.bot -column $i -row 0 -sticky ew -padx 10
  179. grid columnconfigure $dlg.bot $i -weight 1
  180. # We boost the size of some Mac buttons for l&f
  181. if {$windowingsystem eq "aqua"} {
  182. if {($name eq "ok") || ($name eq "dismiss")} {
  183. grid columnconfigure $dlg.bot $i -minsize 90
  184. }
  185. grid configure $dlg.$name -pady 7
  186. }
  187. incr i
  188. }
  189. # The "OK" button is the default for this dialog.
  190. $dlg.ok configure -default active
  191. bind $dlg <Return> [namespace code {Return ok 0}]
  192. bind $dlg <Escape> [namespace code {Return dismiss 1}]
  193. bind $dlg <Destroy> [namespace code {Destroy %W}]
  194. bind $dlg.function <Return> [namespace code {ReturnInDetails %W}]
  195. $dlg.function configure -command [namespace code Details]
  196. # 6. Withdraw the window, then update all the geometry information
  197. # so we know how big it wants to be, then center the window in the
  198. # display (Motif style) and de-iconify it.
  199. ::tk::PlaceWindow $dlg
  200. # 7. Set a grab and claim the focus too.
  201. ::tk::SetFocusGrab $dlg $dlg.ok
  202. # 8. Ensure that we are topmost.
  203. raise $dlg
  204. if {[tk windowingsystem] eq "win32"} {
  205. # Place it topmost if we aren't at the top of the stacking
  206. # order to ensure that it's seen
  207. if {[lindex [wm stackorder .] end] ne "$dlg"} {
  208. wm attributes $dlg -topmost 1
  209. }
  210. }
  211. # 9. Wait for the user to respond, then restore the focus and
  212. # return the index of the selected button. Restore the focus
  213. # before deleting the window, since otherwise the window manager
  214. # may take the focus away so we can't redirect it. Finally,
  215. # restore any grab that was in effect.
  216. vwait [namespace which -variable button]
  217. set copy $button; # Save a copy...
  218. ::tk::RestoreFocusGrab $dlg $dlg.ok destroy
  219. if {$copy == 1} {
  220. return -code break
  221. }
  222. }
  223. namespace eval :: {
  224. # Fool the indexer
  225. proc bgerror err {}
  226. rename bgerror {}
  227. namespace import ::tk::dialog::error::bgerror
  228. }