<commit_before>package gui

/*
#cgo pkg-config: gdk-3.0
#include <gdk/gdk.h>
*/
import "C"

import (
	"container/list"
	"fmt"
	"github.com/amrhassan/psmpc/logging"
	"github.com/amrhassan/psmpc/mpdinfo"
	"github.com/amrhassan/psmpc/resources"
	"github.com/conformal/gotk3/gdk"
	"github.com/conformal/gotk3/glib"
	"github.com/conformal/gotk3/gtk"
	"os"
	"unsafe"
)

var logger = logging.New("gui")

/*
 * The paths where the static resources are looked up from. The paths are tried in the order
 * they are listed in, and the first one that exists is used.
 */
var static_resource_file_paths = []string{
	".",
	"~/.local/share/psmpc/",
	"/usr/local/share/psmpc/",
	"/usr/share/psmpc/",
}

// An action type label
type Action int

// Handlers for fired actions from the GUI
type ActionHandler func([]interface{})

// Player actions that can be performed from the GUI
const (
	ACTION_PLAY      = iota
	ACTION_PAUSE     = iota
	ACTION_PLAYPAUSE = iota
	ACTION_NEXT      = iota
	ACTION_PREVIOUS  = iota
	ACTION_QUIT      = iota
)

type GUI struct {
	builder                    *gtk.Builder
	main_window                *gtk.Window
	title_label                *gtk.Label
	artist_label               *gtk.Label
	stopped_header             *gtk.Box
	playback_header            *gtk.Box
	controls_box               *gtk.Box
	artist_box                 *gtk.Box
	previous_button            *gtk.Button
	playpause_button           *gtk.Button
	next_button                *gtk.Button
	play_image                 *gtk.Image
	pause_image                *gtk.Image
	status_icon                *gtk.StatusIcon
	album_box                  *gtk.Box
	album_label                *gtk.Label
	album_art_image            *gtk.Image
	registered_action_handlers map[Action]*list.List
	buttonKeyMap               map[int]Action
	elapsedProgressBar         *gtk.ProgressBar
	resourceManager            *resources.ResourceManager
	currentSong                *mpdinfo.CurrentSong
}

func error_panic(message string, err error) {
	panic(message + ": " + err.Error())
}

func path_exists(path string) bool {
	_, path_error := os.Stat(path)
	return path_error == nil || os.IsExist(path_error)
}

// A global cahe of static resources. Used by get_static_resource_path()
var staticResources = make(map[string]string)

func get_static_resource_path(resourceName string) string {

	entry, exists := staticResources[resourceName]
	if exists {
		return entry
	}

	for _, path := range static_resource_file_paths {
		full_path := path + "/gui/" + resourceName
		if path_exists(full_path) {
			logger.Info("Using %s file from: %s", resourceName, full_path)
			staticResources[resourceName] = full_path
			return full_path
		}
	}

	logger.Fatal("Can't find %s file", resourceName)
	return ""
}

// Constructs and initializes a new GUI instance
// Args:
// 	buttonKeyMap: a mapping between button key values and GUI actions
func NewGUI(buttonKeyMap map[int]Action) *GUI {
	gtk.Init(nil)

	builder, err := gtk.BuilderNew()
	if err != nil {
		error_panic("Failed to create gtk.Builder", err)
	}

	err = builder.AddFromFile(get_static_resource_path("ui.glade"))
	if err != nil {
		error_panic("Failed to load the Glade UI file", err)
	}

	getGtkObject := func(name string) glib.IObject {
		object, err := builder.GetObject(name)
		if err != nil {
			panic("Failed to retrieve GTK object " + name)
		}
		return object
	}

	main_window := getGtkObject("main_window").(*gtk.Window)
	main_window.SetIconFromFile(get_static_resource_path("icon.png"))

	artist_label := getGtkObject("artist_label").(*gtk.Label)
	title_label := getGtkObject("title_label").(*gtk.Label)
	playback_header := getGtkObject("playback_header_box").(*gtk.Box)
	controls_box := getGtkObject("controls_box").(*gtk.Box)
	artist_box := getGtkObject("artist_box").(*gtk.Box)
	playpause_button := getGtkObject("play-pause_button").(*gtk.Button)
	previous_button := getGtkObject("previous_button").(*gtk.Button)
	next_button := getGtkObject("next_button").(*gtk.Button)
	pause_image, _ := gtk.ImageNewFromIconName("gtk-media-pause", gtk.ICON_SIZE_BUTTON)
	play_image := getGtkObject("play_image").(*gtk.Image)
	status_icon, _ := gtk.StatusIconNewFromFile(get_static_resource_path("icon.png"))
	album_box := getGtkObject("album_box").(*gtk.Box)
	album_label := getGtkObject("album_label").(*gtk.Label)
	album_art_image := getGtkObject("album_art").(*gtk.Image)
	elapsed_progress_bar := getGtkObject("elapsed_progressbar").(*gtk.ProgressBar)

	return &GUI{
		builder:                    builder,
		main_window:                main_window,
		title_label:                title_label,
		artist_label:               artist_label,
		controls_box:               controls_box,
		artist_box:                 artist_box,
		playpause_button:           playpause_button,
		previous_button:            previous_button,
		next_button:                next_button,
		playback_header:            playback_header,
		play_image:                 play_image,
		pause_image:                pause_image,
		status_icon:                status_icon,
		album_box:                  album_box,
		album_label:                album_label,
		album_art_image:            album_art_image,
		elapsedProgressBar:         elapsed_progress_bar,
		registered_action_handlers: make(map[Action]*list.List),
		buttonKeyMap:               buttonKeyMap,
		resourceManager:            resources.NewResourceManager(),
	}
}

// Initiates the GUI
func (this *GUI) Run() {

	this.main_window.Connect("destroy", func() {
		this.fireAction(ACTION_QUIT)
	})

	this.status_icon.Connect("activate", func() {
		logger.Debug("Status Icon clicked!")
		this.main_window.Show()

		this.main_window.SetUrgencyHint(true)
	})

	this.playpause_button.Connect("clicked", func() {
		this.fireAction(ACTION_PLAYPAUSE)
	})

	this.previous_button.Connect("clicked", func() {
		this.fireAction(ACTION_PREVIOUS)
	})

	this.next_button.Connect("clicked", func() {
		this.fireAction(ACTION_NEXT)
	})

	this.main_window.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) {
		key := extract_key_from_gdk_event(event)
		logger.Debug("Key press: %v", key)

		action, mapped := this.buttonKeyMap[key.value]

		if mapped {
			this.fireAction(action)
		} else if key.value == 65307 {
			this.main_window.Hide()
		}
	})

	this.main_window.ShowAll()
	gtk.Main()<commit_msg>Revert "Status Icon click sets the main window to URGENT"<commit_after>package gui

/*
#cgo pkg-config: gdk-3.0
#include <gdk/gdk.h>
*/
import "C"

import (
	"container/list"
	"fmt"
	"github.com/amrhassan/psmpc/logging"
	"github.com/amrhassan/psmpc/mpdinfo"
	"github.com/amrhassan/psmpc/resources"
	"github.com/conformal/gotk3/gdk"
	"github.com/conformal/gotk3/glib"
	"github.com/conformal/gotk3/gtk"
	"os"
	"unsafe"
)

var logger = logging.New("gui")

/*
 * The paths where the static resources are looked up from. The paths are tried in the order
 * they are listed in, and the first one that exists is used.
 */
var static_resource_file_paths = []string{
	".",
	"~/.local/share/psmpc/",
	"/usr/local/share/psmpc/",
	"/usr/share/psmpc/",
}

// An action type label
type Action int

// Handlers for fired actions from the GUI
type ActionHandler func([]interface{})

// Player actions that can be performed from the GUI
const (
	ACTION_PLAY      = iota
	ACTION_PAUSE     = iota
	ACTION_PLAYPAUSE = iota
	ACTION_NEXT      = iota
	ACTION_PREVIOUS  = iota
	ACTION_QUIT      = iota
)

type GUI struct {
	builder                    *gtk.Builder
	main_window                *gtk.Window
	title_label                *gtk.Label
	artist_label               *gtk.Label
	stopped_header             *gtk.Box
	playback_header            *gtk.Box
	controls_box               *gtk.Box
	artist_box                 *gtk.Box
	previous_button            *gtk.Button
	playpause_button           *gtk.Button
	next_button                *gtk.Button
	play_image                 *gtk.Image
	pause_image                *gtk.Image
	status_icon                *gtk.StatusIcon
	album_box                  *gtk.Box
	album_label                *gtk.Label
	album_art_image            *gtk.Image
	registered_action_handlers map[Action]*list.List
	buttonKeyMap               map[int]Action
	elapsedProgressBar         *gtk.ProgressBar
	resourceManager            *resources.ResourceManager
	currentSong                *mpdinfo.CurrentSong
}

func error_panic(message string, err error) {
	panic(message + ": " + err.Error())
}

func path_exists(path string) bool {
	_, path_error := os.Stat(path)
	return path_error == nil || os.IsExist(path_error)
}

// A global cahe of static resources. Used by get_static_resource_path()
var staticResources = make(map[string]string)

func get_static_resource_path(resourceName string) string {

	entry, exists := staticResources[resourceName]
	if exists {
		return entry
	}

	for _, path := range static_resource_file_paths {
		full_path := path + "/gui/" + resourceName
		if path_exists(full_path) {
			logger.Info("Using %s file from: %s", resourceName, full_path)
			staticResources[resourceName] = full_path
			return full_path
		}
	}

	logger.Fatal("Can't find %s file", resourceName)
	return ""
}

// Constructs and initializes a new GUI instance
// Args:
// 	buttonKeyMap: a mapping between button key values and GUI actions
func NewGUI(buttonKeyMap map[int]Action) *GUI {
	gtk.Init(nil)

	builder, err := gtk.BuilderNew()
	if err != nil {
		error_panic("Failed to create gtk.Builder", err)
	}

	err = builder.AddFromFile(get_static_resource_path("ui.glade"))
	if err != nil {
		error_panic("Failed to load the Glade UI file", err)
	}

	getGtkObject := func(name string) glib.IObject {
		object, err := builder.GetObject(name)
		if err != nil {
			panic("Failed to retrieve GTK object " + name)
		}
		return object
	}

	main_window := getGtkObject("main_window").(*gtk.Window)
	main_window.SetIconFromFile(get_static_resource_path("icon.png"))

	artist_label := getGtkObject("artist_label").(*gtk.Label)
	title_label := getGtkObject("title_label").(*gtk.Label)
	playback_header := getGtkObject("playback_header_box").(*gtk.Box)
	controls_box := getGtkObject("controls_box").(*gtk.Box)
	artist_box := getGtkObject("artist_box").(*gtk.Box)
	playpause_button := getGtkObject("play-pause_button").(*gtk.Button)
	previous_button := getGtkObject("previous_button").(*gtk.Button)
	next_button := getGtkObject("next_button").(*gtk.Button)
	pause_image, _ := gtk.ImageNewFromIconName("gtk-media-pause", gtk.ICON_SIZE_BUTTON)
	play_image := getGtkObject("play_image").(*gtk.Image)
	status_icon, _ := gtk.StatusIconNewFromFile(get_static_resource_path("icon.png"))
	album_box := getGtkObject("album_box").(*gtk.Box)
	album_label := getGtkObject("album_label").(*gtk.Label)
	album_art_image := getGtkObject("album_art").(*gtk.Image)
	elapsed_progress_bar := getGtkObject("elapsed_progressbar").(*gtk.ProgressBar)

	return &GUI{
		builder:                    builder,
		main_window:                main_window,
		title_label:                title_label,
		artist_label:               artist_label,
		controls_box:               controls_box,
		artist_box:                 artist_box,
		playpause_button:           playpause_button,
		previous_button:            previous_button,
		next_button:                next_button,
		playback_header:            playback_header,
		play_image:                 play_image,
		pause_image:                pause_image,
		status_icon:                status_icon,
		album_box:                  album_box,
		album_label:                album_label,
		album_art_image:            album_art_image,
		elapsedProgressBar:         elapsed_progress_bar,
		registered_action_handlers: make(map[Action]*list.List),
		buttonKeyMap:               buttonKeyMap,
		resourceManager:            resources.NewResourceManager(),
	}
}

// Initiates the GUI
func (this *GUI) Run() {

	this.main_window.Connect("destroy", func() {
		this.fireAction(ACTION_QUIT)
	})

	this.status_icon.Connect("activate", func() {
		logger.Debug("Status Icon clicked!")
		this.main_window.SetVisible(!this.main_window.GetVisible())
	})

	this.playpause_button.Connect("clicked", func() {
		this.fireAction(ACTION_PLAYPAUSE)
	})

	this.previous_button.Connect("clicked", func() {
		this.fireAction(ACTION_PREVIOUS)
	})

	this.next_button.Connect("clicked", func() {
		this.fireAction(ACTION_NEXT)
	})

	this.main_window.Connect("key-release-event", func(window *gtk.Window, event *gdk.Event) {
		key := extract_key_from_gdk_event(event)
		logger.Debug("Key press: %v", key)

		action, mapped := this.buttonKeyMap[key.value]

		if mapped {
			this.fireAction(action)
		} else if key.value == 65307 {
			this.main_window.Hide()
		}
	})

	this.main_window.ShowAll()
	gtk.Main()
{'fraction_non_alphanumeric': 0.11587239070500197, 'fraction_numerical': 0.0015754233950374162, 'mean_word_length': 3.217940199335548, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3888226', 'n_tokens_mistral': 4472, 'n_tokens_neox': 4173, 'n_words': 811}