blob: 3d3cf0e9a420e3294eabb4409a3c2ea94d9c8413 [file] [log] [blame]
# File src/library/tcltk/R/utils.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2018 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
tk_select.list <-
function(choices, preselect = NULL, multiple = FALSE, title = NULL)
{
have_ttk <- as.character(tcl("info", "tclversion")) >= "8.5"
if(!have_ttk) ttkbutton <- tkbutton
lvar <- tclVar()
tclObj(lvar) <- choices
oldmode <- tclServiceMode(FALSE)
dlg <- tktoplevel()
tkwm.title(dlg, title)
tkwm.deiconify(dlg)
tkgrab.set(dlg)
tkfocus(dlg)
if(!is.null(title) && nzchar(title)) {
lab <- if(have_ttk) ttklabel(dlg, text = title, foreground = "blue")
else tklabel(dlg, text = title, fg = "blue")
tkpack(lab, side="top")
}
onOK <- function() {
res <- 1L + as.integer(tkcurselection(box))
ans.select_list <<- choices[res]
tkgrab.release(dlg)
tkdestroy(dlg)
}
onCancel <- function() {
tkgrab.release(dlg)
tkdestroy(dlg)
}
buttons <- tkframe(dlg)
tkpack(buttons, side="bottom")
OK <- ttkbutton(buttons, text = gettext("OK"), width = 6, command = onOK)
Cancel <- ttkbutton(buttons, text = gettext("Cancel"), command = onCancel)
tkpack(OK, Cancel, side="left", fill="x", padx="2m")
scht <- as.numeric(tclvalue(tkwinfo("screenheight", dlg))) - 200L
## allow for win furniture and buttons, and for e.g. KDE panel
ht <- min(length(choices), scht %/% 20) # a guess of font height
s_mode <- if(multiple) "multiple" else "single"
box <- tklistbox(dlg, height = ht,
listvariable = lvar, bg = "white", setgrid = 1,
selectmode = s_mode)
tmp <- tcl("font", "metrics", tkcget(box, font=NULL))
## fudge factor here seems to be 1 on Windows, 3 on X11.
tmp <- as.numeric(sub(".*linespace ([0-9]+) .*", "\\1", tclvalue(tmp)))+3
ht <- min(length(choices), scht %/% tmp)
tkdestroy(box)
if(ht < length(choices)) {
scr <- if(have_ttk) ttkscrollbar(dlg, command = function(...) tkyview(box, ...))
else tkscrollbar(dlg, repeatinterval=5, command = function(...) tkyview(box, ...))
box <- tklistbox(dlg, height = ht, width = 0,
listvariable = lvar, bg = "white", setgrid = 1,
selectmode = s_mode,
yscrollcommand = function(...)tkset(scr,...))
tkpack(box, side="left", fill="both", expand=TRUE)
tkpack(scr, side="right", fill="y")
} else {
box <- tklistbox(dlg, height = ht, width = 0,
listvariable = lvar, bg = "white",
selectmode = s_mode)
tkpack(box, side="left", fill="both")
}
preselect <- match(preselect, choices)
preselect <- preselect[preselect > 0L] - 1L # 0-based
if(length(preselect)) {
for(i in preselect) tkselection.set(box, i)
## ensure first (and usally only) pre-selection is visible
tkyview(box, preselect[1L])
}
ans.select_list <- character() # avoid name conflicts
tkbind(dlg, "<Destroy>", onCancel)
tkbind(box, "<Double-ButtonPress-1>", onOK)
tkfocus(box)
tclServiceMode(oldmode)
tkwait.window(dlg)
Sys.sleep(0.1) # allow time for window to be removed.
if(!multiple && !length(ans.select_list)) ans.select_list <- ""
ans.select_list
}
tkProgressBar <- function(title = "R progress bar", label = "",
min = 0, max = 1, initial = 0, width = 300)
{
useText <- FALSE
have_ttk <- as.character(tcl("info", "tclversion")) >= "8.5"
if(!have_ttk && as.character(tclRequire("PBar")) == "FALSE") useText <- TRUE
.win <- tktoplevel()
.val <- initial
.killed <- FALSE
tkwm.geometry(.win, sprintf("%dx80", width+40))
tkwm.title(.win, title)
fn <- tkfont.create(family="helvetica", size=12)
if(useText) {
## currently unused
.lab <- tklabel(.win, text=label, font=fn, padx=20)
tkpack(.lab, side = "left")
fn2 <- tkfont.create(family="helvetica", size=16)
.vlab <- tklabel(.win, text="0%", font=fn2, padx=20)
tkpack(.vlab, side = "right")
up <- function(value) {
if(!is.finite(value) || value < min || value > max) return()
.val <<- value
tkconfigure(.vlab,
text=sprintf("%d%%",
round(100*(value - min)/(max - min))))
}
} else {
.lab <- tklabel(.win, text=label, font=fn, pady=10)
.tkval <- tclVar(0)
tkpack(.lab, side="top")
tkpack(tklabel(.win, text="", font = fn), side="bottom")
pBar <- if(have_ttk) ttkprogressbar(.win, length=width, variable=.tkval) else tkwidget(.win, "ProgressBar", width=width, variable=.tkval)
tkpack(pBar, side="bottom")
up <- function(value) {
if(!is.finite(value) || value < min || value > max) return()
.val <<- value
tclvalue(.tkval) <<- 100*(value - min)/(max - min)
}
}
getVal <- function() .val
kill <- function() if(!.killed) {tkdestroy(.win); .killed <<- TRUE}
title <- function(title) tkwm.title(.win, title)
lab <- function(label) tkconfigure(.lab, text=label)
tkbind(.win, "<Destroy>", kill)
up(initial)
structure(list(getVal=getVal, up=up, title=title, label=lab, kill=kill),
class = "tkProgressBar")
}
getTkProgressBar <- function(pb)
{
if(!inherits(pb, "tkProgressBar"))
stop(gettextf("'pb' is not from class %s",
dQuote("tkProgressBar")),
domain = NA)
pb$getVal()
}
setTkProgressBar <- function(pb, value, title = NULL, label = NULL)
{
if(!inherits(pb, "tkProgressBar"))
stop(gettextf("'pb' is not from class %s",
dQuote("tkProgressBar")),
domain = NA)
oldval <- pb$getVal()
pb$up(value)
if(!is.null(title)) pb$title(title)
if(!is.null(label)) pb$label(label)
tcl("update", "idletasks")
invisible(oldval)
}
close.tkProgressBar <- function(con, ...)
{
con$kill()
invisible(NULL)
}
tk_choose.files <-
function(default = '', caption = 'Select files', multi = TRUE,
filters = NULL, index = 1)
{
args <- list("tk_getOpenFile", title = caption, multiple = multi)
if(nzchar(default)) args <- c(args, initialdir = dirname(default),
initialfile = basename(default))
if(!is.null(filters)) {
if(!is.character(filters) || length(dim(filters)) != 2 || ncol(filters) != 2)
stop("'filters' must be a 2-column character matrix")
f <- filters
f[] <- paste0("{", filters, "}")
ff <- apply(f, 1, paste, collapse = " ")
fff <- paste0("{", ff, "}")
args <- c(args, filetypes = paste(fff, collapse = " "))
}
res <- tclvalue(do.call(tcl, args))
if(nzchar(res))
if(multi) {
## Filenames with spaces will be surrounded by { }
ans <- character()
pat <- "([^{])*\\{([^}]*)\\}(.*)"
while(grepl(pat, res)) {
ans <- c(ans, sub(pat, "\\2", res))
res <- sub(pat, "\\1\\3", res)
}
ans <- c(ans, strsplit(res, " ", fixed = TRUE)[[1]])
ans[nzchar(ans)]
} else res
else character()
}
tk_choose.dir <- function(default = '', caption = 'Select directory')
{
res <- tclvalue(tcl("tk_chooseDirectory", initialdir = default, title = caption))
if(nzchar(res)) res else NA_character_
}
tk_messageBox <-
function(type = c("ok", "okcancel", "yesno", "yesnocancel",
"retrycancel", "abortretryignore"),
message, caption = "", default = "", ...)
{
type <- match.arg(type)
args <- list("tk_messageBox", type=type, message=message,
title=caption, ...)
if(nzchar(default)) args <- c(args, default=default)
tclvalue(do.call("tcl", args))
}
tclVersion <- function() as.character(tcl("info", "patchlevel"))