2014-02-16 14:55:51 -06:00
// +build !windows,!darwin,!plan9
// 16 february 2014
2014-03-12 20:55:45 -05:00
2014-02-19 10:41:10 -06:00
package ui
2014-02-16 14:55:51 -06:00
import (
2014-03-12 16:31:13 -05:00
"time"
2014-02-16 14:55:51 -06:00
)
2014-04-01 15:30:38 -05:00
// #include "gtk_unix.h"
import "C"
2014-02-16 14:55:51 -06:00
type sysData struct {
cSysData
2014-06-10 09:22:30 -05:00
widget * C . GtkWidget
container * C . GtkWidget // for moving
pulse chan bool // for sysData.progressPulse()
clickCounter clickCounter // for Areas
2014-06-06 22:31:50 -05:00
// we probably don't need to save these, but we'll do so for sysData.preferredSize() just in case
2014-06-10 09:22:30 -05:00
areawidth int
areaheight int
2014-02-16 14:55:51 -06:00
}
type classData struct {
2014-06-10 09:22:30 -05:00
make func ( ) * C . GtkWidget
makeAlt func ( ) * C . GtkWidget
setText func ( widget * C . GtkWidget , text string )
text func ( widget * C . GtkWidget ) string
append func ( widget * C . GtkWidget , text string )
insert func ( widget * C . GtkWidget , index int , text string )
selected func ( widget * C . GtkWidget ) int
selMulti func ( widget * C . GtkWidget ) [ ] int
smtexts func ( widget * C . GtkWidget ) [ ] string
delete func ( widget * C . GtkWidget , index int )
len func ( widget * C . GtkWidget ) int
2014-02-16 15:23:49 -06:00
// ...
2014-06-10 09:22:30 -05:00
signals callbackMap
child func ( widget * C . GtkWidget ) * C . GtkWidget
childsigs callbackMap
2014-02-16 14:55:51 -06:00
}
var classTypes = [ nctypes ] * classData {
2014-06-10 09:22:30 -05:00
c_window : & classData {
make : gtk_window_new ,
setText : gtk_window_set_title ,
text : gtk_window_get_title ,
signals : callbackMap {
"delete-event" : window_delete_event_callback ,
"configure-event" : window_configure_event_callback ,
2014-02-16 15:23:49 -06:00
} ,
2014-02-16 14:55:51 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_button : & classData {
make : gtk_button_new ,
setText : gtk_button_set_label ,
text : gtk_button_get_label ,
signals : callbackMap {
"clicked" : button_clicked_callback ,
2014-02-16 16:30:58 -06:00
} ,
2014-02-16 14:55:51 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_checkbox : & classData {
make : gtk_check_button_new ,
setText : gtk_button_set_label ,
text : gtk_button_get_label ,
2014-02-16 14:55:51 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_combobox : & classData {
make : gtk_combo_box_text_new ,
makeAlt : gtk_combo_box_text_new_with_entry ,
text : gtk_combo_box_text_get_active_text ,
append : gtk_combo_box_text_append_text ,
insert : gtk_combo_box_text_insert_text ,
selected : gtk_combo_box_get_active ,
delete : gtk_combo_box_text_remove ,
len : gtkComboBoxLen ,
2014-02-16 14:55:51 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_lineedit : & classData {
make : gtk_entry_new ,
makeAlt : gtkPasswordEntryNew ,
setText : gtk_entry_set_text ,
text : gtk_entry_get_text ,
2014-02-16 14:55:51 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_label : & classData {
make : gtk_label_new ,
setText : gtk_label_set_text ,
text : gtk_label_get_text ,
2014-02-16 14:55:51 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_listbox : & classData {
make : gListboxNewSingle ,
makeAlt : gListboxNewMulti ,
text : gListboxText ,
append : gListboxAppend ,
insert : gListboxInsert ,
selMulti : gListboxSelectedMulti ,
smtexts : gListboxSelMultiTexts ,
delete : gListboxDelete ,
len : gListboxLen ,
2014-02-24 23:48:23 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_progressbar : & classData {
make : gtk_progress_bar_new ,
2014-02-16 14:55:51 -06:00
} ,
2014-06-10 09:22:30 -05:00
c_area : & classData {
make : gtkAreaNew ,
child : gtkAreaGetControl ,
childsigs : callbackMap {
"draw" : area_draw_callback ,
"button-press-event" : area_button_press_event_callback ,
"button-release-event" : area_button_release_event_callback ,
"motion-notify-event" : area_motion_notify_event_callback ,
"enter-notify-event" : area_enterleave_notify_event_callback ,
"leave-notify-event" : area_enterleave_notify_event_callback ,
"key-press-event" : area_key_press_event_callback ,
"key-release-event" : area_key_release_event_callback ,
2014-03-14 22:15:24 -05:00
} ,
2014-03-14 22:06:51 -05:00
} ,
2014-02-16 14:55:51 -06:00
}
2014-04-01 15:43:56 -05:00
func ( s * sysData ) make ( window * sysData ) error {
2014-02-16 14:55:51 -06:00
ct := classTypes [ s . ctype ]
2014-04-01 15:30:38 -05:00
ret := make ( chan * C . GtkWidget )
2014-02-16 14:55:51 -06:00
defer close ( ret )
uitask <- func ( ) {
2014-02-16 19:27:16 -06:00
if s . alternate {
ret <- ct . makeAlt ( )
return
}
2014-02-16 14:55:51 -06:00
ret <- ct . make ( )
}
s . widget = <- ret
if window == nil {
uitask <- func ( ) {
2014-03-15 13:27:18 -05:00
fixed := gtkNewWindowLayout ( )
2014-02-16 14:55:51 -06:00
gtk_container_add ( s . widget , fixed )
2014-03-14 19:03:02 -05:00
for signame , sigfunc := range ct . signals {
g_signal_connect ( s . widget , signame , sigfunc , s )
2014-02-16 15:23:49 -06:00
}
2014-02-16 14:55:51 -06:00
ret <- fixed
}
s . container = <- ret
} else {
s . container = window . container
2014-02-16 16:30:58 -06:00
uitask <- func ( ) {
2014-03-15 13:27:18 -05:00
gtkAddWidgetToLayout ( s . container , s . widget )
2014-03-14 19:03:02 -05:00
for signame , sigfunc := range ct . signals {
g_signal_connect ( s . widget , signame , sigfunc , s )
2014-02-16 16:30:58 -06:00
}
2014-03-14 22:15:24 -05:00
if ct . child != nil {
child := ct . child ( s . widget )
for signame , sigfunc := range ct . childsigs {
g_signal_connect ( child , signame , sigfunc , s )
}
}
2014-02-16 16:30:58 -06:00
ret <- nil
}
<- ret
2014-02-16 14:55:51 -06:00
}
return nil
}
2014-06-11 13:38:16 -05:00
// see sysData.center()
func ( s * sysData ) resetposition ( ) {
C . gtk_window_set_position ( togtkwindow ( s . widget ) , C . GTK_WIN_POS_NONE )
}
2014-03-09 20:40:14 -05:00
// used for Windows; nothing special needed elsewhere
func ( s * sysData ) firstShow ( ) error {
s . show ( )
return nil
}
2014-03-09 20:56:17 -05:00
func ( s * sysData ) show ( ) {
2014-02-16 14:55:51 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
gtk_widget_show ( s . widget )
2014-06-11 13:38:16 -05:00
s . resetposition ( )
2014-02-16 14:55:51 -06:00
ret <- struct { } { }
}
<- ret
}
2014-03-09 20:56:17 -05:00
func ( s * sysData ) hide ( ) {
2014-02-16 14:55:51 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
gtk_widget_hide ( s . widget )
2014-06-11 13:38:16 -05:00
s . resetposition ( )
2014-02-16 14:55:51 -06:00
ret <- struct { } { }
}
<- ret
}
2014-03-10 09:39:08 -05:00
func ( s * sysData ) setText ( text string ) {
2014-02-16 14:55:51 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . setText ( s . widget , text )
ret <- struct { } { }
}
<- ret
}
2014-03-03 16:44:03 -06:00
func ( s * sysData ) setRect ( x int , y int , width int , height int , winheight int ) error {
2014-03-15 13:27:18 -05:00
gtkMoveWidgetInLayout ( s . container , s . widget , x , y )
2014-02-17 00:40:53 -06:00
gtk_widget_set_size_request ( s . widget , width , height )
2014-02-16 14:55:51 -06:00
return nil
}
func ( s * sysData ) isChecked ( ) bool {
2014-02-16 17:41:29 -06:00
ret := make ( chan bool )
defer close ( ret )
uitask <- func ( ) {
ret <- gtk_toggle_button_get_active ( s . widget )
}
return <- ret
2014-02-16 14:55:51 -06:00
}
func ( s * sysData ) text ( ) string {
2014-02-16 17:57:50 -06:00
ret := make ( chan string )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . text ( s . widget )
}
return <- ret
2014-02-16 14:55:51 -06:00
}
2014-03-09 15:02:17 -05:00
func ( s * sysData ) append ( what string ) {
2014-02-16 18:50:52 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . append ( s . widget , what )
ret <- struct { } { }
}
<- ret
2014-02-16 14:55:51 -06:00
}
2014-03-09 15:02:17 -05:00
func ( s * sysData ) insertBefore ( what string , before int ) {
2014-02-16 18:50:52 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . insert ( s . widget , before , what )
ret <- struct { } { }
}
<- ret
2014-02-16 14:55:51 -06:00
}
func ( s * sysData ) selectedIndex ( ) int {
2014-02-16 18:50:52 -06:00
ret := make ( chan int )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . selected ( s . widget )
}
return <- ret
2014-02-16 14:55:51 -06:00
}
func ( s * sysData ) selectedIndices ( ) [ ] int {
2014-02-16 22:50:54 -06:00
ret := make ( chan [ ] int )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . selMulti ( s . widget )
}
return <- ret
2014-02-16 14:55:51 -06:00
}
func ( s * sysData ) selectedTexts ( ) [ ] string {
2014-02-16 22:50:54 -06:00
ret := make ( chan [ ] string )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . smtexts ( s . widget )
}
return <- ret
2014-02-16 14:55:51 -06:00
}
func ( s * sysData ) setWindowSize ( width int , height int ) error {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
2014-04-15 11:58:17 -05:00
// does not take window geometry into account (and cannot, since the window manager won't give that info away)
// thanks to TingPing in irc.gimp.net/#gtk+
2014-02-16 14:55:51 -06:00
gtk_window_resize ( s . widget , width , height )
ret <- struct { } { }
}
<- ret
return nil
}
2014-03-11 12:50:02 -05:00
func ( s * sysData ) delete ( index int ) {
2014-02-16 18:50:52 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
classTypes [ s . ctype ] . delete ( s . widget , index )
ret <- struct { } { }
}
<- ret
2014-02-16 14:55:51 -06:00
}
2014-02-24 23:48:23 -06:00
2014-03-12 16:31:13 -05:00
// With GTK+, we must manually pulse the indeterminate progressbar ourselves. This goroutine does that.
func ( s * sysData ) progressPulse ( ) {
pulse := func ( ) {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
gtk_progress_bar_pulse ( s . widget )
ret <- struct { } { }
}
<- ret
}
var ticker * time . Ticker
var tickchan <- chan time . Time
2014-04-07 23:21:12 -05:00
// the pulse rate used by Zenity (https://git.gnome.org/browse/zenity/tree/src/progress.c#n69 for blob cbffe08e8337ba1375a0ac7210eff5a2e4313bb8)
const pulseRate = 100 * time . Millisecond
2014-03-12 16:31:13 -05:00
for {
select {
case start := <- s . pulse :
if start {
ticker = time . NewTicker ( pulseRate )
tickchan = ticker . C
2014-06-10 09:22:30 -05:00
pulse ( ) // start the pulse animation now, not 100ms later
2014-03-12 16:31:13 -05:00
} else {
if ticker != nil {
ticker . Stop ( )
}
ticker = nil
tickchan = nil
2014-06-10 09:22:30 -05:00
s . pulse <- true // notify sysData.setProgress()
2014-03-12 16:31:13 -05:00
}
case <- tickchan :
pulse ( )
}
}
}
2014-02-24 23:48:23 -06:00
func ( s * sysData ) setProgress ( percent int ) {
2014-03-12 16:31:13 -05:00
if s . pulse == nil {
s . pulse = make ( chan bool )
go s . progressPulse ( )
}
if percent == - 1 {
s . pulse <- true
return
}
s . pulse <- false
2014-06-10 09:22:30 -05:00
<- s . pulse // wait for sysData.progressPulse() to register that
2014-02-24 23:48:23 -06:00
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
gtk_progress_bar_set_fraction ( s . widget , percent )
ret <- struct { } { }
}
<- ret
}
2014-03-08 15:42:57 -06:00
func ( s * sysData ) len ( ) int {
ret := make ( chan int )
defer close ( ret )
uitask <- func ( ) {
ret <- classTypes [ s . ctype ] . len ( s . widget )
}
return <- ret
}
2014-03-23 19:54:11 -05:00
func ( s * sysData ) setAreaSize ( width int , height int ) {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
c := gtkAreaGetControl ( s . widget )
gtk_widget_set_size_request ( c , width , height )
2014-06-10 09:22:30 -05:00
s . areawidth = width // for sysData.preferredSize()
2014-06-06 22:31:50 -05:00
s . areaheight = height
2014-06-09 21:47:45 -05:00
C . gtk_widget_queue_draw ( c )
2014-03-23 19:54:11 -05:00
ret <- struct { } { }
}
<- ret
}
2014-06-09 21:22:02 -05:00
func ( s * sysData ) repaintAll ( ) {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
c := gtkAreaGetControl ( s . widget )
C . gtk_widget_queue_draw ( c )
ret <- struct { } { }
}
<- ret
}
2014-06-11 13:38:16 -05:00
func ( s * sysData ) center ( ) {
ret := make ( chan struct { } )
defer close ( ret )
uitask <- func ( ) {
if C . gtk_widget_get_visible ( s . widget ) == C . FALSE {
// hint to the WM to make it centered when it is shown again
// thanks to Jasper in irc.gimp.net/#gtk+
C . gtk_window_set_position ( togtkwindow ( s . widget ) , C . GTK_WIN_POS_CENTER )
} else {
var width , height C . gint
s . resetposition ( )
//we should be able to use gravity to simplify this, but it doesn't take effect immediately, and adding show calls does nothing (thanks Jasper in irc.gimp.net/#gtk+)
C . gtk_window_get_size ( togtkwindow ( s . widget ) , & width , & height )
C . gtk_window_move ( togtkwindow ( s . widget ) ,
( C . gdk_screen_width ( ) / 2 ) - ( width / 2 ) ,
( C . gdk_screen_height ( ) / 2 ) - ( width / 2 ) )
}
ret <- struct { } { }
}
<- ret
}