// // rhlibrary // // The Radio Helsinki Rivendell Library // // // Copyright (C) 2016 Christian Pointner // // This file is part of rhlibrary. // // rhlibrary 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 3 of the License, or // any later version. // // rhlibrary 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. // // You should have received a copy of the GNU General Public License // along with rhlibrary. If not, see . // package main import ( "time" "code.helsinki.at/rhrd-go/player" "github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/gtk" ) var ( playIcon *gtk.Image = nil pauseIcon *gtk.Image = nil ) func addPlayPauseButton(box *gtk.Box, p *player.PlayerChan) (err error) { var btn *gtk.Button if btn, err = gtk.ButtonNew(); err != nil { return } if playIcon, err = gtk.ImageNewFromIconName("media-playback-start", gtk.ICON_SIZE_LARGE_TOOLBAR); err != nil { return } if pauseIcon, err = gtk.ImageNewFromIconName("media-playback-pause", gtk.ICON_SIZE_LARGE_TOOLBAR); err != nil { return } btn.SetSizeRequest(42, 42) btn.SetImage(playIcon) btn.SetSensitive(false) btn.Connect("clicked", func(_ *gtk.Button) { if err := p.PlayPause(); err != nil { rhdl.Println(err) // TODO: feedback at GUI? } }) p.AddStateChangeHandler(func(state player.State, _ interface{}) bool { glib.IdleAdd(func() { switch state { case player.IDLE: btn.SetImage(playIcon) btn.SetSensitive(false) case player.PAUSED: btn.SetImage(playIcon) btn.SetSensitive(true) case player.PLAYING: btn.SetImage(pauseIcon) btn.SetSensitive(true) } }) return true }, nil) box.PackStart(btn, false, false, 0) return } func addStopButton(box *gtk.Box, p *player.PlayerChan) (err error) { var btn *gtk.Button if btn, err = gtk.ButtonNewFromIconName("media-playback-stop", gtk.ICON_SIZE_LARGE_TOOLBAR); err != nil { return } btn.SetSizeRequest(42, 42) btn.SetSensitive(false) btn.Connect("clicked", func(_ *gtk.Button) { if err := p.Stop(); err != nil { rhdl.Println(err) // TODO: feedback at GUI? } }) p.AddStateChangeHandler(func(state player.State, _ interface{}) bool { glib.IdleAdd(func() { switch state { case player.IDLE: btn.SetSensitive(false) case player.PAUSED: btn.SetSensitive(true) case player.PLAYING: btn.SetSensitive(true) } }) return true }, nil) box.PackStart(btn, false, false, 0) return } func addButtons(grid *gtk.Grid, p *player.PlayerChan) (err error) { var box *gtk.Box if box, err = gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 2); err != nil { return } if err = addPlayPauseButton(box, p); err != nil { return } if err = addStopButton(box, p); err != nil { return } grid.Attach(box, 1, 1, 1, 1) return } func addMeter(grid *gtk.Grid, p *player.PlayerChan) error { widget, err := getVUMeterWidget(p) if err != nil { return err } grid.Attach(widget, 2, 1, 1, 1) return nil } func getPlayTimeString(duration, pos time.Duration, countdown bool) string { t := time.Unix(0, 0).UTC() if countdown { return t.Add(duration).Add(-1 * pos).Format("-15:04:05.0") } return t.Add(pos).Format(" 15:04:05.0") } func addPlayTime(grid *gtk.Grid, p *player.PlayerChan) (err error) { var btn *gtk.Button if btn, err = gtk.ButtonNew(); err != nil { return err } countdown := false duration := time.Duration(0) pos := time.Duration(0) p.AddUpdateHandler(func(d time.Duration, p time.Duration, _ player.Meter, _ interface{}) bool { glib.IdleAdd(func() { duration = d pos = p btn.SetLabel(getPlayTimeString(duration, pos, countdown)) }) return true }, nil) btn.SetSizeRequest(128, 42) btn.SetRelief(gtk.RELIEF_NONE) btn.SetFocusOnClick(false) btn.SetLabel(getPlayTimeString(duration, pos, countdown)) btn.Connect("clicked", func(_ *gtk.Button) { countdown = !countdown btn.SetLabel(getPlayTimeString(duration, pos, countdown)) }) if err = setCssStyle(&btn.Widget, ".button { border: 1px solid; border-color: #a1a1a1; font-weight: bold; font-size: 1.3em; }"); err != nil { return } grid.Attach(btn, 3, 1, 1, 1) return } func addScrubber(grid *gtk.Grid, p *player.PlayerChan) (err error) { var scale *gtk.Scale if scale, err = gtk.ScaleNew(gtk.ORIENTATION_HORIZONTAL, nil); err != nil { return } if err = scale.SetProperty("draw-value", false); err != nil { return } scale.SetRange(0, 1) scale.SetValue(0) scale.Connect("change-value", func(s *gtk.Scale, _ interface{}, pos float64) { if err := p.Seek(pos); err != nil { rhdl.Println(err) // TODO: feedback at GUI? } }) scale.SetSensitive(false) p.AddUpdateHandler(func(duration time.Duration, pos time.Duration, _ player.Meter, _ interface{}) bool { glib.IdleAdd(func() { if duration > 0 { scale.SetValue(float64(pos) / float64(duration)) } else { scale.SetValue(0) } }) return true }, nil) p.AddStateChangeHandler(func(state player.State, _ interface{}) bool { glib.IdleAdd(func() { switch state { case player.IDLE: scale.SetSensitive(false) case player.PAUSED: scale.SetSensitive(true) case player.PLAYING: scale.SetSensitive(true) } }) return true }, nil) grid.Attach(scale, 1, 2, 3, 1) return } func getPlayerWidget(p *player.PlayerChan) (gtk.IWidget, error) { var err error var grid *gtk.Grid if grid, err = gtk.GridNew(); err != nil { return nil, err } grid.SetOrientation(gtk.ORIENTATION_HORIZONTAL) grid.SetRowSpacing(8) grid.SetColumnSpacing(13) if err = addButtons(grid, p); err != nil { return nil, err } if err = addMeter(grid, p); err != nil { return nil, err } if err = addPlayTime(grid, p); err != nil { return nil, err } if err = addScrubber(grid, p); err != nil { return nil, err } grid.SetHAlign(gtk.ALIGN_CENTER) return grid, nil }