A Snit Widget which can embed almost any application on X-Window window managers edit
Yes, I use a tiling window manager - I3, gnu screen for managing terminals. But still I feel do need to better group related windows together. That's what SnitXWindow for. You can embed an arrange all related windows into one Tk window. You can put them in tabs, or paned windows. You can move or activate your Terminals, editor, your pdf viewer your browser window, your spreedsheet etc all related to your project in one step.
This extension works only on X-Windows, requires
TkXext and
Snit and optionally the Surf webbrowser, Mupdf or Zathura pdf viewers.
Hopefully that X will be alive for some time despite of Wayland ...
Sample run: You can type direct mupdf or zathura in the Window entries and a file dialog will be open. Or you type part of a window name into the entry, the window will be embedded. On the notebook you can use Ctrl-1 to add a new empty frame ready to catch other windows, with Ctrl-2 you add an panedwindow entry below.
# Created By : Dr. Detlef Groth
# Created : Mon Jan 29 05:11:44 2018
# Last Modified : <180216.0722>
#
# Description : A snit widget for embedding other X-applications
#
#
# Notes : xprop require to get window properties
# using the commandline tool xprop
# Requirements : xprop , should be installed on X-Window systems
#
# History : 0.1 initial release 2018-01-29
# : 0.2 embedding zathura, mupdf in the empty field
# window title using xprop
# : 0.3 avoid reparenting already embeded window
#
#
# Copyright (c) 2018 Dr. Detlef Groth.
#
# License BSD
package require Tk
package require TkXext
package require snit
package require SnitTtkNotebook
package require SnitXSurf
package require SnitXMupdf
package provide SnitXWindow 0.3
snit::widget SnitXWindow {
option -cmd xterm
option -embedoption ""
option -searchtitle ""
option -cmdargs ""
variable app ""
variable pid ""
variable WinTitle ""
variable DynTitle ""
constructor {args} {
$self configurelist $args
pack [frame $win.status] -side top -fill x -expand false
pack [label $win.status.lb -textvariable [myvar DynTitle]] -side left -fill x -expand false
# install check
set ok [auto_execok $options(-cmd)]
if {$options(-cmd) != ""} {
if {$ok eq ""} {
return -code error "please install $options(-cmd)"
}
$self LoadApp
} else {
$self EmbedByTitleFrame
}
bind $win <Enter> [mymethod EnterWin]
bind $win <Control-r> [mymethod LoadApp]
bind $win <Control-o> [mymethod ReopenWithNewFile]
}
method EmbedByTitleFrame {} {
if {![winfo exists $win.top]} {
frame $win.top
pack [label $win.top.lbl -text "Window Title:"] -padx 3 -side left
pack [entry $win.top.ent -textvariable [myvar WinTitle]] -side left -padx 3
pack [button $win.top.btn -text "Embed Window" -command [mymethod EmbedTitle]] -side left -padx 3
pack $win.top -fill x -expand false -side top
}
}
method ReadPipe {chan} {
set d [read $chan]
foreach line [split $d \n] {
if {[regexp {^WM_NAME.STRING. = "(.+)"} $line m title] } {
set DynTitle $title
}
}
if {[eof $chan]} {
fileevent $chan readable {}
close $chan
}
}
method getWinState {} {
set state Normal
set res [exec xprop -id 0x$app]
foreach line [split $res "\n"] {
if {[regexp {window state: ([^ ]+)} $line -> state]} {
break
}
}
return $state
}
method getWinProperties {} {
set xpin [open "|xprop -id 0x$app -spy" r]
fconfigure $xpin -blocking 0 -buffering line
fileevent $xpin readable [mymethod ReadPipe $xpin]
}
method EmbedTitle {} {
if {[regexp {surf +(.+)} $WinTitle -> url]} {
SnitXSurf $win.f -home $url
} elseif {[regexp {zathura} $WinTitle] || [regexp {mupdf} $WinTitle] } {
set types {
{{Pdf Files} {.pdf} }
{{Ps Files} {.ps} }
{{Comic Files} {.cbz} }
{{All Files} { *} }
}
if {[regexp {mupdf} $WinTitle] } {
set types {
{{Pdf Files} {.pdf} }
{{Ebook Files} {.epub} }
{{Comic Files} {.cbz} }
{{All Files} { *} }
}
}
set filename [tk_getOpenFile -filetypes $types]
if {$filename != ""} {
if {[regexp {zathura} $WinTitle]} {
SnitXWindow $win.f -cmd zathura -cmdargs $filename -searchtitle $filename
} else {
SnitXMupdf $win.f -infile $filename -standalone false
}
}
} else {
set app [TkXext.find.window $WinTitle];
if {![winfo exists $win.f]} {
frame $win.f -width 200 -height 200 -container 1
}
while {$app eq ""} {
after 200
set app [TkXext.find.window $WinTitle];
if {[incr x] > 10} {
break
}
}
if {$app eq "" || $app == 0} {
tk_messageBox -title "Info!" -icon info -message "Unable to find window '$WinTitle'!\nHint: Try a * at the End!" -type ok
return
}
TkXext.reparent.window $app [winfo id $win.f]
bind $win.f <Configure> [list TkXext.resize.window $app %w %h]
bind $win.f <Destroy> [list TkXext.delete.or.kill $app]
bind $win <Control-r> [list pack $win.top]
$self getWinProperties
}
pack $win.f -fill both -expand 1 -side top
pack forget $win.top
}
method ReopenWithNewFile {} {
if {$options(-cmd) eq "mupdf"} {
set types {
{{Pdf Files} {.pdf} }
{{Epub Files} {.epub} }
{{All Files} * }
}
set filename [tk_getOpenFile -filetypes $types]
if {$filename != ""} {
puts $filename
set options(-cmdargs) [file nativename $filename]
set options(-searchtitle) "[file tail [file nativename $filename]]*"
if {[winfo exists $win.f]} {
destroy $win.f
}
$self LoadApp
}
}
}
method LoadApp {} {
if {![winfo exists $win.f]} {
frame $win.f -width 200 -height 200 -container 1
}
if {$options(-embedoption) eq ""} {
set pid [exec $options(-cmd) {*}$options(-cmdargs) &]
}
set app ""
while {$app eq ""} {
after 200
set app [TkXext.find.window $options(-searchtitle)];
set state [$self getWinState]
if { $state eq "Withdrawn" } {
# window was already catched
# wait for the fresh one!!
# don't steal
set app ""
}
if {[incr x] > 100} {
break
}
}
if {$app eq ""} {
destroy $win.f
return -code error "unable to find the $options(-cmd) window"
}
TkXext.reparent.window $app [winfo id $win.f]
bind $win.f <Configure> [list TkXext.resize.window $app %w %h]
pack $win.f -fill both -expand 1
if {$options(-cmd) eq "urxvt"} {
pack forget $win.status
} else {
$self getWinProperties
}
}
method EnterWin {} {
catch {focus -force $win}
}
destructor {
try {
exec kill $pid
} finally {
try {
# TkXext.delete.or.kill $app
# did not work
}
}
destroy $win
}
}
if {$argv0 eq [info script]} {
if {[llength $argv] <= 1 } {
# simple window manager
# just create a simple workspace
# ready to add addtional windows
set pw1 [panedwindow .pw1 -orient horizontal -width 100 -height 100]
set nb [SnitTtkNotebook .pw1.nb]
set pw2 [panedwindow $nb.pw2 -orient vertical -width 100 -height 100]
SnitXWindow $pw2.term -cmd xterm -cmdargs "-title myurxvt -rv" -searchtitle myurxvt
SnitXWindow $pw2.empty -cmd ""
SnitXWindow $pw1.editor -cmd ""
#
SnitXSurf $nb.surf -home http://www.duckduckgo.com
SnitXWindow $nb.empty -cmd ""
$nb add $pw2 -text "Xterm/App"
$nb add $nb.surf -text "Surf"
$nb add $nb.empty -text "Empty"
$pw1 add $nb
$pw1 add $pw1.editor
$pw2 add $pw2.term
$pw2 add $pw2.empty
pack $pw1 -side top -fill both -expand true
# example mupdf: SnitXWindow $pw2.mupdf -cmd mupdf -cmdargs "/path/to/Berg-2006-BMCGenomics-PCA.pdf" -searchtitle "Berg-2006-BMCGenomics-PCA.pdf*"
# example editor: SnitXWindow $pw1.me -cmd $me -cmdargs "-cdefault test.c " -searchtitle "MicroEmacs: default: test.c"
# example urxvt: SnitXWindow $nb.urxvt -cmd urxvt -cmdargs "-title myurxvt -rv" -searchtitle myurxvt
# example gnumeric: SnitXWindow $nb.gnum -cmd gnumeric -cmdargs "/path/to/file.xls" -searchtitle "*file.xls*"
proc addTab {} {
global nb
set x [llength [$nb tabs]]
SnitXWindow $nb.empty$x -cmd ""
$nb add $nb.empty$x -text "Tab $x"
}
proc addSplitTab {} {
global nb
set x [llength [$nb tabs]]
set pw [panedwindow $nb.empty$x -orient vertical]
set x1 [SnitXWindow $nb.empty$x.x1 -cmd ""]
set x2 [SnitXWindow $nb.empty$x.x2 -cmd ""]
$nb add $nb.empty$x -text "Tab $x"
$pw add $x1 $x2
}
$nb bind <Control-t> "" ;# remove binding for new tab
$nb bind <Control-KeyPress-1> addTab
$nb bind <Control-KeyPress-2> addSplitTab
}
}