2016-10-04 15:42:36 +05:00
// URTator - Urban Terror server browser and game launcher, written in
// Go.
//
// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n)
// All rights reserved.
//
// Licensed under Terms and Conditions of GNU General Public License
// version 3 or any higher.
// ToDo: put full text of license here.
package ui
import (
// stdlib
"encoding/base64"
"errors"
"fmt"
2016-10-06 13:55:03 +05:00
"reflect"
2016-10-05 20:46:02 +05:00
"runtime"
2016-10-04 15:42:36 +05:00
"strconv"
2016-10-04 23:39:32 +05:00
"strings"
2016-10-04 15:42:36 +05:00
// Local
"github.com/pztrn/urtrator/common"
"github.com/pztrn/urtrator/datamodels"
// Other
"github.com/mattn/go-gtk/gdkpixbuf"
"github.com/mattn/go-gtk/glib"
"github.com/mattn/go-gtk/gtk"
)
type MainWindow struct {
// Gamemodes.
gamemodes map [ string ] string
// Widgets.
// The window itself.
window * gtk . Window
// Vertical Box.
vbox * gtk . VBox
// Main menu.
menubar * gtk . MenuBar
// Toolbar
toolbar * gtk . Toolbar
// Horizontal box for main window.
hpane * gtk . HPaned
// Tab widget.
tab_widget * gtk . Notebook
// Tabs list.
tabs map [ string ] * gtk . Frame
// All servers widget.
all_servers * gtk . TreeView
// Favorite servers widget.
fav_servers * gtk . TreeView
// Statusbar.
statusbar * gtk . Statusbar
// Statusbar context ID.
statusbar_context_id uint
// Profiles combobox.
profiles * gtk . ComboBoxText
// Checkbox for hiding/showing offline servers in 'Servers' tab list.
all_servers_hide_offline * gtk . CheckButton
// Checkbox for hiding/showing offline servers in 'Favorites' tab list.
fav_servers_hide_offline * gtk . CheckButton
// Game launch button.
launch_button * gtk . Button
2016-10-04 23:39:32 +05:00
// Quick connect: server address
qc_server_address * gtk . Entry
// Quick connect: password
qc_password * gtk . Entry
2016-10-05 01:03:46 +05:00
// Tray icon.
tray_icon * gtk . StatusIcon
// Tray menu.
tray_menu * gtk . Menu
2016-10-04 15:42:36 +05:00
// Storages.
// All servers store.
all_servers_store * gtk . ListStore
// All servers sortable store.
all_servers_store_sortable * gtk . TreeSortable
// Favorites
fav_servers_store * gtk . ListStore
// Dialogs.
options_dialog * OptionsDialog
2016-10-04 23:39:32 +05:00
// Favorite server editing.
favorite_dialog * FavoriteDialog
2016-10-04 15:42:36 +05:00
// Other
// Old profiles count.
old_profiles_count int
2016-10-05 02:16:02 +05:00
// Window size.
window_width int
window_height int
// Window position.
window_pos_x int
window_pos_y int
// Main pane delimiter position. It is calculated like:
//
// window_width - pane_position
//
// so we will get same right pane width even if we will resize
// main window. On resize and restore it will be set like:
//
// window_width - m.pane_negative_position
pane_negative_position int
2016-10-05 01:03:46 +05:00
// Flags.
// Window is hidden?
hidden bool
2016-10-04 15:42:36 +05:00
}
func ( m * MainWindow ) addToFavorites ( ) {
fmt . Println ( "Adding server to favorites..." )
2016-10-04 23:39:32 +05:00
current_tab := m . tab_widget . GetTabLabelText ( m . tab_widget . GetNthPage ( m . tab_widget . GetCurrentPage ( ) ) )
// Getting server's address from list.
sel := m . all_servers . GetSelection ( )
model := m . all_servers . GetModel ( )
if strings . Contains ( current_tab , "Favorites" ) {
// Getting server's address from list.
sel = m . fav_servers . GetSelection ( )
model = m . fav_servers . GetModel ( )
}
iter := new ( gtk . TreeIter )
_ = sel . GetSelected ( iter )
// Getting server address.
var srv_addr string
srv_addr_gval := glib . ValueFromNative ( srv_addr )
model . GetValue ( iter , 7 , srv_addr_gval )
server_address := srv_addr_gval . GetString ( )
// Getting server from database.
m . favorite_dialog = & FavoriteDialog { }
if len ( server_address ) > 0 {
servers := [ ] datamodels . Server { }
address := strings . Split ( server_address , ":" ) [ 0 ]
port := strings . Split ( server_address , ":" ) [ 1 ]
err1 := ctx . Database . Db . Select ( & servers , ctx . Database . Db . Rebind ( "SELECT * FROM servers WHERE ip=? AND port=?" ) , address , port )
if err1 != nil {
fmt . Println ( err1 . Error ( ) )
}
m . favorite_dialog . InitializeUpdate ( & servers [ 0 ] )
} else {
m . favorite_dialog . InitializeNew ( )
}
2016-10-04 15:42:36 +05:00
}
2016-10-06 13:55:03 +05:00
// Executes when delimiter for two panes is moved, to calculate VALID
// position.
2016-10-05 02:16:02 +05:00
func ( m * MainWindow ) checkMainPanePosition ( ) {
m . pane_negative_position = m . window_width - m . hpane . GetPosition ( )
}
2016-10-06 13:55:03 +05:00
// Executes when main window is moved or resized.
// Also calculating pane delimiter position and set it to avoid
// widgets hell :).
2016-10-05 02:16:02 +05:00
func ( m * MainWindow ) checkPositionAndSize ( ) {
m . window . GetPosition ( & m . window_pos_x , & m . window_pos_y )
m . window . GetSize ( & m . window_width , & m . window_height )
m . hpane . SetPosition ( m . window_width - m . pane_negative_position )
}
2016-10-06 13:55:03 +05:00
// Executes on URTrator shutdown.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) Close ( ) {
2016-10-05 02:16:02 +05:00
// Save window parameters.
ctx . Cfg . Cfg [ "/mainwindow/width" ] = strconv . Itoa ( m . window_width )
ctx . Cfg . Cfg [ "/mainwindow/height" ] = strconv . Itoa ( m . window_height )
ctx . Cfg . Cfg [ "/mainwindow/position_x" ] = strconv . Itoa ( m . window_pos_x )
ctx . Cfg . Cfg [ "/mainwindow/position_y" ] = strconv . Itoa ( m . window_pos_y )
ctx . Cfg . Cfg [ "/mainwindow/pane_negative_position" ] = strconv . Itoa ( m . pane_negative_position )
2016-10-04 15:42:36 +05:00
ctx . Close ( )
}
2016-10-06 13:55:03 +05:00
// Deleting server from favorites.
2016-10-04 23:39:32 +05:00
func ( m * MainWindow ) deleteFromFavorites ( ) {
fmt . Println ( "Removing server from favorites..." )
current_tab := m . tab_widget . GetTabLabelText ( m . tab_widget . GetNthPage ( m . tab_widget . GetCurrentPage ( ) ) )
// Assuming that deletion was called from "Servers" tab by default.
sel := m . all_servers . GetSelection ( )
model := m . all_servers . GetModel ( )
if strings . Contains ( current_tab , "Favorites" ) {
// Getting server's address from list.
sel = m . fav_servers . GetSelection ( )
model = m . fav_servers . GetModel ( )
}
iter := new ( gtk . TreeIter )
_ = sel . GetSelected ( iter )
// Getting server address.
var srv_addr string
srv_addr_gval := glib . ValueFromNative ( srv_addr )
model . GetValue ( iter , 7 , srv_addr_gval )
server_address := srv_addr_gval . GetString ( )
var not_favorited bool = false
if len ( server_address ) > 0 {
address := strings . Split ( server_address , ":" ) [ 0 ]
port := strings . Split ( server_address , ":" ) [ 1 ]
srv := [ ] datamodels . Server { }
err := ctx . Database . Db . Select ( & srv , ctx . Database . Db . Rebind ( "SELECT * FROM servers WHERE ip=? AND port=?" ) , address , port )
if err != nil {
fmt . Println ( err . Error ( ) )
}
if srv [ 0 ] . Favorite == "1" {
ctx . Database . Db . MustExec ( ctx . Database . Db . Rebind ( "UPDATE servers SET favorite='0' WHERE ip=? AND port=?" ) , address , port )
ctx . Eventer . LaunchEvent ( "loadFavoriteServers" )
} else {
not_favorited = true
}
} else {
not_favorited = true
}
if not_favorited {
mbox_string := "Cannot delete server from favorites.\n\nServer isn't favorited."
d := gtk . NewMessageDialog ( m . window , gtk . DIALOG_MODAL , gtk . MESSAGE_INFO , gtk . BUTTONS_OK , mbox_string )
d . Response ( func ( ) {
d . Destroy ( )
} )
d . Run ( )
}
}
2016-10-06 13:55:03 +05:00
// Drop database data.
// ToDo: extend so we should have an ability to decide what to drop.
2016-10-04 23:39:32 +05:00
func ( m * MainWindow ) dropDatabasesData ( ) {
fmt . Println ( "Dropping database data..." )
var will_continue bool = false
mbox_string := "You are about to drop whole database data.\n\nAfter clicking \"YES\" ALL data in database (servers, profiles, settings, etc.)\nwill be lost FOREVER. Are you sure?"
d := gtk . NewMessageDialog ( m . window , gtk . DIALOG_MODAL , gtk . MESSAGE_WARNING , gtk . BUTTONS_YES_NO , mbox_string )
d . Connect ( "response" , func ( resp * glib . CallbackContext ) {
if resp . Args ( 0 ) == 4294967287 {
will_continue = false
} else {
will_continue = true
}
} )
d . Response ( func ( ) {
d . Destroy ( )
} )
d . Run ( )
if will_continue {
ctx . Database . Db . MustExec ( "DELETE FROM servers" )
ctx . Database . Db . MustExec ( "DELETE FROM settings" )
ctx . Database . Db . MustExec ( "DELETE FROM urt_profiles" )
ctx . Eventer . LaunchEvent ( "loadProfiles" )
ctx . Eventer . LaunchEvent ( "loadAllServers" )
ctx . Eventer . LaunchEvent ( "loadFavoriteServers" )
}
}
2016-10-06 13:55:03 +05:00
// Executes on "Edit favorite server" click.
2016-10-05 00:04:24 +05:00
func ( m * MainWindow ) editFavorite ( ) {
fmt . Println ( "Editing favorite server..." )
sel := m . fav_servers . GetSelection ( )
model := m . fav_servers . GetModel ( )
iter := new ( gtk . TreeIter )
_ = sel . GetSelected ( iter )
// Getting server address.
var srv_addr string
srv_addr_gval := glib . ValueFromNative ( srv_addr )
model . GetValue ( iter , 7 , srv_addr_gval )
server_address := srv_addr_gval . GetString ( )
if len ( server_address ) > 0 {
address := strings . Split ( server_address , ":" ) [ 0 ]
port := strings . Split ( server_address , ":" ) [ 1 ]
srv := [ ] datamodels . Server { }
err := ctx . Database . Db . Select ( & srv , ctx . Database . Db . Rebind ( "SELECT * FROM servers WHERE ip=? AND port=?" ) , address , port )
if err != nil {
fmt . Println ( err . Error ( ) )
}
m . favorite_dialog = & FavoriteDialog { }
m . favorite_dialog . InitializeUpdate ( & srv [ 0 ] )
}
}
2016-10-06 13:55:03 +05:00
// Executes when "Hide offline servers" checkbox changed it's state on
// "Servers" tab.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) hideOfflineAllServers ( ) {
fmt . Println ( "(Un)Hiding offline servers in 'Servers' tab..." )
2016-10-05 20:46:02 +05:00
if m . all_servers_hide_offline . GetActive ( ) {
ctx . Cfg . Cfg [ "/serverslist/all_servers/hide_offline" ] = "1"
} else {
ctx . Cfg . Cfg [ "/serverslist/all_servers/hide_offline" ] = "0"
}
2016-10-04 15:42:36 +05:00
ctx . Eventer . LaunchEvent ( "loadAllServers" )
}
2016-10-06 13:55:03 +05:00
// Executes when "Hide offline servers" checkbox changed it's state on
// "Favorites" tab.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) hideOfflineFavoriteServers ( ) {
fmt . Println ( "(Un)Hiding offline servers in 'Favorite' tab..." )
2016-10-05 20:46:02 +05:00
if m . fav_servers_hide_offline . GetActive ( ) {
ctx . Cfg . Cfg [ "/serverslist/favorite/hide_offline" ] = "1"
} else {
ctx . Cfg . Cfg [ "/serverslist/favorite/hide_offline" ] = "0"
}
2016-10-04 23:39:32 +05:00
ctx . Eventer . LaunchEvent ( "loadFavoriteServers" )
2016-10-04 15:42:36 +05:00
}
2016-10-06 13:55:03 +05:00
// Main window initialization.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) Initialize ( ) {
m . initializeStorages ( )
gtk . Init ( nil )
m . window = gtk . NewWindow ( gtk . WINDOW_TOPLEVEL )
m . window . SetTitle ( "URTrator" )
m . window . Connect ( "destroy" , m . Close )
m . vbox = gtk . NewVBox ( false , 0 )
// Load program icon from base64.
icon_bytes , _ := base64 . StdEncoding . DecodeString ( common . Logo )
icon_pixbuf := gdkpixbuf . NewLoader ( )
icon_pixbuf . Write ( icon_bytes )
logo = icon_pixbuf . GetPixbuf ( )
m . window . SetIcon ( logo )
2016-10-05 02:16:02 +05:00
m . window . Connect ( "configure-event" , m . checkPositionAndSize )
// Restoring window position.
var win_pos_x_str string = "0"
var win_pos_y_str string = "0"
saved_win_pos_x_str , ok := ctx . Cfg . Cfg [ "/mainwindow/position_x" ]
if ok {
win_pos_x_str = saved_win_pos_x_str
}
saved_win_pos_y_str , ok := ctx . Cfg . Cfg [ "/mainwindow/position_y" ]
if ok {
win_pos_y_str = saved_win_pos_y_str
}
win_pos_x , _ := strconv . Atoi ( win_pos_x_str )
win_pos_y , _ := strconv . Atoi ( win_pos_y_str )
m . window . Move ( win_pos_x , win_pos_y )
// Restoring window size.
var win_size_width_str string = "1000"
var win_size_height_str string = "600"
saved_win_size_width_str , ok := ctx . Cfg . Cfg [ "/mainwindow/width" ]
if ok {
win_size_width_str = saved_win_size_width_str
}
saved_win_size_height_str , ok := ctx . Cfg . Cfg [ "/mainwindow/height" ]
if ok {
win_size_height_str = saved_win_size_height_str
}
m . window_width , _ = strconv . Atoi ( win_size_width_str )
m . window_height , _ = strconv . Atoi ( win_size_height_str )
m . window . SetDefaultSize ( m . window_width , m . window_height )
2016-10-04 15:42:36 +05:00
// Dialogs initialization.
m . options_dialog = & OptionsDialog { }
// Main menu.
m . InitializeMainMenu ( )
// Toolbar.
m . InitializeToolbar ( )
2016-10-05 02:16:02 +05:00
m . hpane = gtk . NewHPaned ( )
m . vbox . PackStart ( m . hpane , true , true , 5 )
m . hpane . Connect ( "event" , m . checkMainPanePosition )
// Restore pane position.
// We will restore saved thing, or will use "window_width - 150".
saved_pane_pos , ok := ctx . Cfg . Cfg [ "/mainwindow/pane_negative_position" ]
if ok {
pane_negative_pos , _ := strconv . Atoi ( saved_pane_pos )
m . hpane . SetPosition ( m . window_width - pane_negative_pos )
} else {
var w , h int = 0 , 0
m . window . GetSize ( & w , & h )
m . hpane . SetPosition ( w - 150 )
}
2016-10-04 15:42:36 +05:00
// Tabs initialization.
m . InitializeTabs ( )
// Sidebar initialization.
m . initializeSidebar ( )
2016-10-05 01:03:46 +05:00
// Tray icon.
if ctx . Cfg . Cfg [ "/general/show_tray_icon" ] == "1" {
m . initializeTrayIcon ( )
}
2016-10-06 13:55:03 +05:00
// Events.
m . initializeEvents ( )
2016-10-04 15:42:36 +05:00
// Game profiles and launch button.
profile_and_launch_hbox := gtk . NewHBox ( false , 0 )
m . vbox . PackStart ( profile_and_launch_hbox , false , true , 5 )
// Separator
sep := gtk . NewHSeparator ( )
profile_and_launch_hbox . PackStart ( sep , true , true , 5 )
// Profile selection.
profiles_label := gtk . NewLabel ( "Game profile:" )
m . profiles = gtk . NewComboBoxText ( )
2016-10-04 19:21:48 +05:00
m . profiles . SetTooltipText ( "Profile which will be used for launching" )
2016-10-04 15:42:36 +05:00
profile_and_launch_hbox . PackStart ( profiles_label , false , true , 5 )
profile_and_launch_hbox . PackStart ( m . profiles , false , true , 5 )
ctx . Eventer . AddEventHandler ( "loadProfiles" , m . loadProfiles )
// One more separator.
sepp := gtk . NewVSeparator ( )
profile_and_launch_hbox . PackStart ( sepp , false , true , 5 )
// Game launching button.
m . launch_button = gtk . NewButtonWithLabel ( "Launch!" )
2016-10-04 19:21:48 +05:00
m . launch_button . SetTooltipText ( "Launch Urban Terror" )
2016-10-04 15:42:36 +05:00
m . launch_button . Clicked ( m . launchGame )
profile_and_launch_hbox . PackStart ( m . launch_button , false , true , 5 )
// Statusbar.
m . statusbar = gtk . NewStatusbar ( )
m . vbox . PackStart ( m . statusbar , false , true , 0 )
m . statusbar_context_id = m . statusbar . GetContextId ( "Status Bar" )
m . statusbar . Push ( m . statusbar_context_id , "URTrator is ready" )
m . window . Add ( m . vbox )
m . window . ShowAll ( )
// Launch events.
ctx . Eventer . LaunchEvent ( "loadProfiles" )
2016-10-06 13:55:03 +05:00
ctx . Eventer . LaunchEvent ( "loadServersIntoCache" )
2016-10-04 15:42:36 +05:00
ctx . Eventer . LaunchEvent ( "loadAllServers" )
2016-10-04 23:39:32 +05:00
ctx . Eventer . LaunchEvent ( "loadFavoriteServers" )
2016-10-04 15:42:36 +05:00
gtk . Main ( )
}
2016-10-06 13:55:03 +05:00
// Events initialization.
func ( m * MainWindow ) initializeEvents ( ) {
fmt . Println ( "Initializing events..." )
ctx . Eventer . AddEventHandler ( "loadAllServers" , m . loadAllServers )
ctx . Eventer . AddEventHandler ( "loadFavoriteServers" , m . loadFavoriteServers )
ctx . Eventer . AddEventHandler ( "serversUpdateCompleted" , m . serversUpdateCompleted )
}
// Main menu initialization.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) InitializeMainMenu ( ) {
m . menubar = gtk . NewMenuBar ( )
m . vbox . PackStart ( m . menubar , false , false , 0 )
// File menu.
fm := gtk . NewMenuItemWithMnemonic ( "File" )
m . menubar . Append ( fm )
file_menu := gtk . NewMenu ( )
fm . SetSubmenu ( file_menu )
// Options.
options_menu_item := gtk . NewMenuItemWithMnemonic ( "_Options" )
file_menu . Append ( options_menu_item )
options_menu_item . Connect ( "activate" , m . options_dialog . ShowOptionsDialog )
2016-10-04 23:39:32 +05:00
// Separator.
file_menu_sep1 := gtk . NewSeparatorMenuItem ( )
file_menu . Append ( file_menu_sep1 )
2016-10-04 15:42:36 +05:00
// Exit.
exit_menu_item := gtk . NewMenuItemWithMnemonic ( "E_xit" )
file_menu . Append ( exit_menu_item )
exit_menu_item . Connect ( "activate" , m . Close )
// About menu.
am := gtk . NewMenuItemWithMnemonic ( "_About" )
m . menubar . Append ( am )
about_menu := gtk . NewMenu ( )
am . SetSubmenu ( about_menu )
// About app item.
about_app_item := gtk . NewMenuItemWithMnemonic ( "About _URTrator..." )
about_menu . Append ( about_app_item )
about_app_item . Connect ( "activate" , ShowAboutDialog )
2016-10-04 23:39:32 +05:00
// Separator.
about_menu_sep1 := gtk . NewSeparatorMenuItem ( )
about_menu . Append ( about_menu_sep1 )
// Drop databases thing.
about_menu_drop_database_data_item := gtk . NewMenuItemWithMnemonic ( "Drop database data..." )
about_menu . Append ( about_menu_drop_database_data_item )
about_menu_drop_database_data_item . Connect ( "activate" , m . dropDatabasesData )
2016-10-04 15:42:36 +05:00
}
2016-10-06 13:55:03 +05:00
// Sidebar (with quick connect and server's information) initialization.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) initializeSidebar ( ) {
sidebar_vbox := gtk . NewVBox ( false , 0 )
2016-10-04 23:39:32 +05:00
// Quick connect frame.
quick_connect_frame := gtk . NewFrame ( "Quick connect" )
sidebar_vbox . PackStart ( quick_connect_frame , true , true , 5 )
qc_vbox := gtk . NewVBox ( false , 0 )
quick_connect_frame . Add ( qc_vbox )
// Server address.
srv_tooltip := "Server address we will connect to"
srv_label := gtk . NewLabel ( "Server address:" )
srv_label . SetTooltipText ( srv_tooltip )
qc_vbox . PackStart ( srv_label , false , true , 5 )
m . qc_server_address = gtk . NewEntry ( )
m . qc_server_address . SetTooltipText ( srv_tooltip )
qc_vbox . PackStart ( m . qc_server_address , false , true , 5 )
// Password.
pass_tooltip := "Password we will use for server"
pass_label := gtk . NewLabel ( "Password:" )
pass_label . SetTooltipText ( pass_tooltip )
qc_vbox . PackStart ( pass_label , false , true , 5 )
m . qc_password = gtk . NewEntry ( )
m . qc_password . SetTooltipText ( pass_tooltip )
qc_vbox . PackStart ( m . qc_password , false , true , 5 )
2016-10-04 15:42:36 +05:00
m . hpane . Add2 ( sidebar_vbox )
}
// Initializes internal storages.
func ( m * MainWindow ) initializeStorages ( ) {
// Gamemodes.
m . gamemodes = make ( map [ string ] string )
m . gamemodes = map [ string ] string {
"1" : "Last Man Standing" ,
"2" : "Free For All" ,
"3" : "Team DM" ,
"4" : "Team Survivor" ,
"5" : "Follow The Leader" ,
"6" : "Cap'n'Hold" ,
"7" : "Capture The Flag" ,
"8" : "Bomb" ,
"9" : "Jump" ,
"10" : "Freeze Tag" ,
"11" : "Gun Game" ,
"12" : "Instagib" ,
}
// Frames storage.
m . tabs = make ( map [ string ] * gtk . Frame )
m . tabs [ "dummy" ] = gtk . NewFrame ( "dummy" )
delete ( m . tabs , "dummy" )
// Servers tab list view storage.
// Structure:
// Server status icon|Server name|Mode|Map|Players|Ping|Version
m . all_servers_store = gtk . NewListStore ( gdkpixbuf . GetType ( ) , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING )
m . all_servers_store_sortable = gtk . NewTreeSortable ( m . all_servers_store )
// Same as above, but for favorite servers.
m . fav_servers_store = gtk . NewListStore ( gdkpixbuf . GetType ( ) , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING , glib . G_TYPE_STRING )
// Profiles count after filling combobox. Defaulting to 0.
m . old_profiles_count = 0
2016-10-05 01:03:46 +05:00
// Window hidden flag.
m . hidden = false
2016-10-04 15:42:36 +05:00
}
2016-10-06 13:55:03 +05:00
// Tabs widget initialization, including all child widgets.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) InitializeTabs ( ) {
// Create tabs widget.
m . tab_widget = gtk . NewNotebook ( )
tab_allsrv_hbox := gtk . NewHBox ( false , 0 )
swin1 := gtk . NewScrolledWindow ( nil , nil )
m . all_servers = gtk . NewTreeView ( )
swin1 . Add ( m . all_servers )
tab_allsrv_hbox . PackStart ( swin1 , true , true , 5 )
m . tab_widget . AppendPage ( tab_allsrv_hbox , gtk . NewLabel ( "Servers" ) )
m . all_servers . SetModel ( m . all_servers_store )
m . all_servers . AppendColumn ( gtk . NewTreeViewColumnWithAttributes ( "Status" , gtk . NewCellRendererPixbuf ( ) , "pixbuf" , 0 ) )
2016-10-05 20:46:02 +05:00
all_srv_name_column := gtk . NewTreeViewColumnWithAttributes ( "Name" , gtk . NewCellRendererText ( ) , "markup" , 1 )
2016-10-04 15:42:36 +05:00
all_srv_name_column . SetSortColumnId ( 1 )
m . all_servers . AppendColumn ( all_srv_name_column )
all_gamemode_column := gtk . NewTreeViewColumnWithAttributes ( "Mode" , gtk . NewCellRendererText ( ) , "text" , 2 )
all_gamemode_column . SetSortColumnId ( 2 )
m . all_servers . AppendColumn ( all_gamemode_column )
all_map_column := gtk . NewTreeViewColumnWithAttributes ( "Map" , gtk . NewCellRendererText ( ) , "text" , 3 )
all_map_column . SetSortColumnId ( 3 )
m . all_servers . AppendColumn ( all_map_column )
// ToDo: custom sorting function.
all_players_column := gtk . NewTreeViewColumnWithAttributes ( "Players" , gtk . NewCellRendererText ( ) , "text" , 4 )
all_players_column . SetSortColumnId ( 4 )
m . all_servers . AppendColumn ( all_players_column )
all_ping_column := gtk . NewTreeViewColumnWithAttributes ( "Ping" , gtk . NewCellRendererText ( ) , "text" , 5 )
all_ping_column . SetSortColumnId ( 5 )
m . all_servers . AppendColumn ( all_ping_column )
all_version_column := gtk . NewTreeViewColumnWithAttributes ( "Version" , gtk . NewCellRendererText ( ) , "text" , 6 )
all_version_column . SetSortColumnId ( 6 )
m . all_servers . AppendColumn ( all_version_column )
all_ip_column := gtk . NewTreeViewColumnWithAttributes ( "IP" , gtk . NewCellRendererText ( ) , "text" , 7 )
all_ip_column . SetSortColumnId ( 7 )
m . all_servers . AppendColumn ( all_ip_column )
// Sorting.
// By default we are sorting by server name.
// ToDo: remembering it to configuration storage.
m . all_servers_store_sortable . SetSortColumnId ( 1 , gtk . SORT_ASCENDING )
// VBox for some servers list controllers.
tab_all_srv_ctl_vbox := gtk . NewVBox ( false , 0 )
tab_allsrv_hbox . PackStart ( tab_all_srv_ctl_vbox , false , true , 5 )
// Checkbox for hiding offline servers.
m . all_servers_hide_offline = gtk . NewCheckButtonWithLabel ( "Hide offline servers" )
2016-10-04 19:21:48 +05:00
m . all_servers_hide_offline . SetTooltipText ( "Hide offline servers on Servers tab" )
2016-10-04 15:42:36 +05:00
tab_all_srv_ctl_vbox . PackStart ( m . all_servers_hide_offline , false , true , 5 )
m . all_servers_hide_offline . Clicked ( m . hideOfflineAllServers )
2016-10-05 20:46:02 +05:00
if ctx . Cfg . Cfg [ "/serverslist/all_servers/hide_offline" ] == "1" {
m . all_servers_hide_offline . SetActive ( true )
}
2016-10-04 15:42:36 +05:00
// Final separator.
ctl_sep := gtk . NewVSeparator ( )
tab_all_srv_ctl_vbox . PackStart ( ctl_sep , true , true , 5 )
// Favorites servers
// ToDo: sorting as in all servers list.
tab_fav_srv_hbox := gtk . NewHBox ( false , 0 )
m . fav_servers = gtk . NewTreeView ( )
swin2 := gtk . NewScrolledWindow ( nil , nil )
swin2 . Add ( m . fav_servers )
tab_fav_srv_hbox . PackStart ( swin2 , true , true , 5 )
m . tab_widget . AppendPage ( tab_fav_srv_hbox , gtk . NewLabel ( "Favorites" ) )
m . fav_servers . SetModel ( m . fav_servers_store )
m . fav_servers . AppendColumn ( gtk . NewTreeViewColumnWithAttributes ( "Status" , gtk . NewCellRendererPixbuf ( ) , "pixbuf" , 0 ) )
2016-10-04 23:39:32 +05:00
2016-10-05 20:46:02 +05:00
fav_name_column := gtk . NewTreeViewColumnWithAttributes ( "Name" , gtk . NewCellRendererText ( ) , "markup" , 1 )
2016-10-04 23:39:32 +05:00
fav_name_column . SetSortColumnId ( 1 )
m . fav_servers . AppendColumn ( fav_name_column )
fav_mode_column := gtk . NewTreeViewColumnWithAttributes ( "Mode" , gtk . NewCellRendererText ( ) , "text" , 2 )
fav_mode_column . SetSortColumnId ( 2 )
m . fav_servers . AppendColumn ( fav_mode_column )
fav_map_column := gtk . NewTreeViewColumnWithAttributes ( "Map" , gtk . NewCellRendererText ( ) , "text" , 3 )
fav_map_column . SetSortColumnId ( 3 )
m . fav_servers . AppendColumn ( fav_map_column )
fav_players_column := gtk . NewTreeViewColumnWithAttributes ( "Players" , gtk . NewCellRendererText ( ) , "text" , 4 )
fav_players_column . SetSortColumnId ( 4 )
m . fav_servers . AppendColumn ( fav_players_column )
fav_ping_column := gtk . NewTreeViewColumnWithAttributes ( "Ping" , gtk . NewCellRendererText ( ) , "text" , 5 )
fav_ping_column . SetSortColumnId ( 5 )
m . fav_servers . AppendColumn ( fav_ping_column )
fav_version_column := gtk . NewTreeViewColumnWithAttributes ( "Version" , gtk . NewCellRendererText ( ) , "text" , 6 )
fav_version_column . SetSortColumnId ( 6 )
m . fav_servers . AppendColumn ( fav_version_column )
fav_ip_column := gtk . NewTreeViewColumnWithAttributes ( "IP" , gtk . NewCellRendererText ( ) , "text" , 7 )
fav_ip_column . SetSortColumnId ( 7 )
m . fav_servers . AppendColumn ( fav_ip_column )
2016-10-04 15:42:36 +05:00
// VBox for some servers list controllers.
tab_fav_srv_ctl_vbox := gtk . NewVBox ( false , 0 )
tab_fav_srv_hbox . PackStart ( tab_fav_srv_ctl_vbox , false , true , 5 )
// Checkbox for hiding offline servers.
m . fav_servers_hide_offline = gtk . NewCheckButtonWithLabel ( "Hide offline servers" )
2016-10-04 19:21:48 +05:00
m . fav_servers_hide_offline . SetTooltipText ( "Hide offline servers on Favorites tab" )
2016-10-04 15:42:36 +05:00
tab_fav_srv_ctl_vbox . PackStart ( m . fav_servers_hide_offline , false , true , 5 )
m . fav_servers_hide_offline . Clicked ( m . hideOfflineFavoriteServers )
2016-10-05 20:46:02 +05:00
if ctx . Cfg . Cfg [ "/serverslist/favorite/hide_offline" ] == "1" {
m . fav_servers_hide_offline . SetActive ( true )
}
2016-10-04 15:42:36 +05:00
// Final separator.
ctl_fav_sep := gtk . NewVSeparator ( )
tab_fav_srv_ctl_vbox . PackStart ( ctl_fav_sep , true , true , 5 )
// Add tab_widget widget to window.
m . hpane . Add1 ( m . tab_widget )
}
2016-10-06 13:55:03 +05:00
// Toolbar initialization.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) InitializeToolbar ( ) {
m . toolbar = gtk . NewToolbar ( )
m . vbox . PackStart ( m . toolbar , false , false , 5 )
// Update servers button.
button_update_all_servers := gtk . NewToolButtonFromStock ( gtk . STOCK_REFRESH )
button_update_all_servers . SetLabel ( "Update all servers" )
2016-10-04 19:21:48 +05:00
button_update_all_servers . SetTooltipText ( "Update all servers in all tabs" )
2016-10-04 15:42:36 +05:00
button_update_all_servers . OnClicked ( m . UpdateServers )
m . toolbar . Insert ( button_update_all_servers , 0 )
// Separator.
separator := gtk . NewSeparatorToolItem ( )
m . toolbar . Insert ( separator , 1 )
// Add server to favorites button.
fav_button := gtk . NewToolButtonFromStock ( gtk . STOCK_ADD )
fav_button . SetLabel ( "Add to favorites" )
2016-10-04 19:21:48 +05:00
fav_button . SetTooltipText ( "Add selected server to favorites" )
2016-10-04 15:42:36 +05:00
fav_button . OnClicked ( m . addToFavorites )
m . toolbar . Insert ( fav_button , 2 )
2016-10-04 23:39:32 +05:00
2016-10-05 00:04:24 +05:00
fav_edit_button := gtk . NewToolButtonFromStock ( gtk . STOCK_EDIT )
fav_edit_button . SetLabel ( "Edit favorite" )
fav_edit_button . SetTooltipText ( "Edit selected favorite server" )
fav_edit_button . OnClicked ( m . editFavorite )
m . toolbar . Insert ( fav_edit_button , 3 )
2016-10-04 23:39:32 +05:00
// Remove server from favorites button.
fav_delete_button := gtk . NewToolButtonFromStock ( gtk . STOCK_REMOVE )
fav_delete_button . SetLabel ( "Remove from favorites" )
fav_delete_button . SetTooltipText ( "Remove selected server from favorites" )
fav_delete_button . OnClicked ( m . deleteFromFavorites )
2016-10-05 00:04:24 +05:00
m . toolbar . Insert ( fav_delete_button , 4 )
2016-10-04 15:42:36 +05:00
}
2016-10-06 13:55:03 +05:00
// Tray icon initialization.
2016-10-05 01:03:46 +05:00
func ( m * MainWindow ) initializeTrayIcon ( ) {
fmt . Println ( "Initializing tray icon..." )
icon_bytes , _ := base64 . StdEncoding . DecodeString ( common . Logo )
icon_pixbuf := gdkpixbuf . NewLoader ( )
icon_pixbuf . Write ( icon_bytes )
logo = icon_pixbuf . GetPixbuf ( )
m . tray_icon = gtk . NewStatusIconFromPixbuf ( logo )
m . tray_icon . SetName ( "URTrator" )
m . tray_icon . SetTitle ( "URTrator" )
m . tray_icon . SetTooltipText ( "URTrator is ready" )
2016-10-05 20:46:02 +05:00
// Tray menu is still buggy on windows, so skipping initialization,
// if OS is Windows.
if runtime . GOOS != "windows" {
m . tray_menu = gtk . NewMenu ( )
// Open/Close URTrator menu item.
open_close_item := gtk . NewMenuItemWithLabel ( "Show / Hide URTrator" )
open_close_item . Connect ( "activate" , m . showHide )
m . tray_menu . Append ( open_close_item )
// Separator
sep1 := gtk . NewSeparatorMenuItem ( )
m . tray_menu . Append ( sep1 )
// Exit menu item.
exit_item := gtk . NewMenuItemWithLabel ( "Exit" )
exit_item . Connect ( "activate" , m . window . Destroy )
m . tray_menu . Append ( exit_item )
// Connect things.
m . tray_icon . Connect ( "activate" , m . showHide )
m . tray_icon . Connect ( "popup-menu" , m . showTrayMenu )
m . tray_menu . ShowAll ( )
}
2016-10-05 01:03:46 +05:00
}
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) launchGame ( ) error {
fmt . Println ( "Launching Urban Terror..." )
// Getting server's name from list.
2016-10-04 23:39:32 +05:00
current_tab := m . tab_widget . GetTabLabelText ( m . tab_widget . GetNthPage ( m . tab_widget . GetCurrentPage ( ) ) )
2016-10-04 15:42:36 +05:00
sel := m . all_servers . GetSelection ( )
model := m . all_servers . GetModel ( )
2016-10-04 23:39:32 +05:00
if strings . Contains ( current_tab , "Favorites" ) {
sel = m . fav_servers . GetSelection ( )
model = m . fav_servers . GetModel ( )
}
2016-10-04 15:42:36 +05:00
iter := new ( gtk . TreeIter )
_ = sel . GetSelected ( iter )
// Getting server name.
var srv_name string
srv_name_gval := glib . ValueFromNative ( srv_name )
model . GetValue ( iter , 1 , srv_name_gval )
server_name := srv_name_gval . GetString ( )
// Getting server address.
var srv_addr string
srv_address_gval := glib . ValueFromNative ( srv_addr )
model . GetValue ( iter , 7 , srv_address_gval )
srv_address := srv_address_gval . GetString ( )
// Getting server's game version.
var srv_game_ver_raw string
srv_game_ver_gval := glib . ValueFromNative ( srv_game_ver_raw )
model . GetValue ( iter , 6 , srv_game_ver_gval )
srv_game_ver := srv_game_ver_gval . GetString ( )
// Check for proper server name. If length == 0: server is offline,
// we should show notification to user.
if len ( server_name ) == 0 {
var will_continue bool = false
mbox_string := "Selected server is offline.\n\nWould you still want to launch Urban Terror?\nIt will just launch a game, without connecting to\nany server."
m := gtk . NewMessageDialog ( m . window , gtk . DIALOG_MODAL , gtk . MESSAGE_ERROR , gtk . BUTTONS_YES_NO , mbox_string )
m . Connect ( "response" , func ( resp * glib . CallbackContext ) {
if resp . Args ( 0 ) == 4294967287 {
will_continue = false
} else {
will_continue = true
}
} )
m . Response ( func ( ) {
m . Destroy ( )
} )
m . Run ( )
if ! will_continue {
return errors . New ( "User declined to connect to offline server" )
}
}
// Getting selected profile's name.
profile_name := m . profiles . GetActiveText ( )
2016-10-04 23:39:32 +05:00
if strings . Contains ( current_tab , "Servers" ) {
// Checking profile name length. If 0 - then stop executing :)
// This check only relevant to "Servers" tab, favorite servers
// have profiles defined (see next).
if len ( profile_name ) == 0 {
mbox_string := "Invalid game profile selected.\n\nPlease, select profile and retry."
m := gtk . NewMessageDialog ( m . window , gtk . DIALOG_MODAL , gtk . MESSAGE_ERROR , gtk . BUTTONS_OK , mbox_string )
m . Response ( func ( ) {
m . Destroy ( )
} )
m . Run ( )
return errors . New ( "User didn't select valid profile." )
}
} else if strings . Contains ( current_tab , "Favorites" ) {
// For favorite servers profile specified in favorite server
// information have higher priority, so we just override it :)
server := [ ] datamodels . Server { }
// All favorites servers should contain IP and Port :)
ip := strings . Split ( srv_address , ":" ) [ 0 ]
port := strings . Split ( srv_address , ":" ) [ 1 ]
err := ctx . Database . Db . Select ( & server , ctx . Database . Db . Rebind ( "SELECT * FROM servers WHERE ip=? AND port=?" ) , ip , port )
if err != nil {
fmt . Println ( err . Error ( ) )
}
profile_name = server [ 0 ] . ProfileToUse
2016-10-04 15:42:36 +05:00
}
// Getting profile data from database.
// ToDo: cache profiles data in runtime.
profile := [ ] datamodels . Profile { }
err := ctx . Database . Db . Select ( & profile , ctx . Database . Db . Rebind ( "SELECT * FROM urt_profiles WHERE name=?" ) , profile_name )
if err != nil {
fmt . Println ( err . Error ( ) )
}
// Check if profile version is valid for selected game server.
if profile [ 0 ] . Version != srv_game_ver {
mbox_string := "Invalid game profile selected.\n\nSelected profile have different game version than server.\nPlease, select valid profile and retry."
m := gtk . NewMessageDialog ( m . window , gtk . DIALOG_MODAL , gtk . MESSAGE_ERROR , gtk . BUTTONS_OK , mbox_string )
m . Response ( func ( ) {
m . Destroy ( )
} )
m . Run ( )
return errors . New ( "User didn't select valid profile, mismatch with server's version." )
}
// Hey, we're ok here! :) Launch Urban Terror!
m . statusbar . Push ( m . statusbar_context_id , "Launching Urban Terror..." )
m . launch_button . SetSensitive ( false )
// ToDo: handling server passwords.
2016-10-04 18:09:14 +05:00
ctx . Launcher . Launch ( & profile [ 0 ] , srv_address , "" , m . unlockInterface )
2016-10-04 15:42:36 +05:00
return nil
}
2016-10-04 23:39:32 +05:00
func ( m * MainWindow ) loadAllServers ( ) {
2016-10-06 13:55:03 +05:00
fmt . Println ( "Loading all servers..." )
2016-10-04 15:42:36 +05:00
// ToDo: do it without clearing.
2016-10-06 13:55:03 +05:00
srv_addrs := reflect . ValueOf ( ctx . Cache . Servers ) . MapKeys ( )
fmt . Println ( srv_addrs )
for _ , server := range ctx . Cache . Servers {
if m . all_servers_hide_offline . GetActive ( ) && server . Server . Name == "" && server . Server . Players == "" {
2016-10-04 15:42:36 +05:00
continue
}
var iter gtk . TreeIter
2016-10-06 13:55:03 +05:00
if ! server . IterSet {
server . TreeIter = iter
m . all_servers_store . Append ( & iter )
server . IterSet = true
} else {
iter = server . TreeIter
}
if server . Server . Name == "" && server . Server . Players == "" {
2016-10-04 15:42:36 +05:00
m . all_servers_store . Set ( & iter , 0 , gtk . NewImage ( ) . RenderIcon ( gtk . STOCK_NO , gtk . ICON_SIZE_SMALL_TOOLBAR , "" ) . GPixbuf )
} else {
m . all_servers_store . Set ( & iter , 0 , gtk . NewImage ( ) . RenderIcon ( gtk . STOCK_OK , gtk . ICON_SIZE_SMALL_TOOLBAR , "" ) . GPixbuf )
}
2016-10-06 13:55:03 +05:00
server_name := ctx . Colorizer . Fix ( server . Server . Name )
m . all_servers_store . Set ( & iter , 1 , server_name )
m . all_servers_store . Set ( & iter , 2 , m . gamemodes [ server . Server . Gamemode ] )
m . all_servers_store . Set ( & iter , 3 , server . Server . Map )
m . all_servers_store . Set ( & iter , 4 , server . Server . Players + "/" + server . Server . Maxplayers )
m . all_servers_store . Set ( & iter , 5 , server . Server . Ping )
m . all_servers_store . Set ( & iter , 6 , server . Server . Version )
m . all_servers_store . Set ( & iter , 7 , server . Server . Ip + ":" + server . Server . Port )
2016-10-04 15:42:36 +05:00
}
}
2016-10-04 23:39:32 +05:00
func ( m * MainWindow ) loadFavoriteServers ( ) {
fmt . Println ( "Loading favorite servers..." )
2016-10-06 13:55:03 +05:00
for _ , server := range ctx . Cache . Servers {
if server . Server . Favorite != "1" {
2016-10-04 23:39:32 +05:00
continue
}
2016-10-06 13:55:03 +05:00
if m . fav_servers_hide_offline . GetActive ( ) && server . Server . Name == "" && server . Server . Players == "" {
2016-10-04 23:39:32 +05:00
continue
}
var iter gtk . TreeIter
2016-10-06 13:55:03 +05:00
if ! server . IterSet {
server . TreeIter = iter
m . fav_servers_store . Append ( & iter )
server . IterSet = true
} else {
iter = server . TreeIter
}
if server . Server . Name == "" && server . Server . Players == "" {
2016-10-04 23:39:32 +05:00
m . fav_servers_store . Set ( & iter , 0 , gtk . NewImage ( ) . RenderIcon ( gtk . STOCK_NO , gtk . ICON_SIZE_SMALL_TOOLBAR , "" ) . GPixbuf )
} else {
m . fav_servers_store . Set ( & iter , 0 , gtk . NewImage ( ) . RenderIcon ( gtk . STOCK_OK , gtk . ICON_SIZE_SMALL_TOOLBAR , "" ) . GPixbuf )
}
2016-10-06 13:55:03 +05:00
server_name := ctx . Colorizer . Fix ( server . Server . Name )
m . fav_servers_store . Set ( & iter , 1 , server_name )
m . fav_servers_store . Set ( & iter , 2 , m . gamemodes [ server . Server . Gamemode ] )
m . fav_servers_store . Set ( & iter , 3 , server . Server . Map )
m . fav_servers_store . Set ( & iter , 4 , server . Server . Players + "/" + server . Server . Maxplayers )
m . fav_servers_store . Set ( & iter , 5 , server . Server . Ping )
m . fav_servers_store . Set ( & iter , 6 , server . Server . Version )
m . fav_servers_store . Set ( & iter , 7 , server . Server . Ip + ":" + server . Server . Port )
2016-10-04 23:39:32 +05:00
}
}
func ( m * MainWindow ) loadProfiles ( ) {
fmt . Println ( "Loading profiles into combobox on MainWindow" )
for i := 0 ; i < m . old_profiles_count ; i ++ {
// ComboBox indexes are shifting on element deletion, so we should
// detele very first element every time.
m . profiles . Remove ( 0 )
}
profiles := [ ] datamodels . Profile { }
err := ctx . Database . Db . Select ( & profiles , "SELECT * FROM urt_profiles" )
if err != nil {
fmt . Println ( err . Error ( ) )
}
for p := range profiles {
m . profiles . AppendText ( profiles [ p ] . Name )
}
m . old_profiles_count = len ( profiles )
fmt . Println ( "Added " + strconv . Itoa ( m . old_profiles_count ) + " profiles" )
}
2016-10-06 13:55:03 +05:00
func ( m * MainWindow ) serversUpdateCompleted ( ) {
m . statusbar . Push ( m . statusbar_context_id , "Servers updated." )
}
2016-10-05 01:03:46 +05:00
func ( m * MainWindow ) showHide ( ) {
if m . hidden {
m . window . Show ( )
m . hidden = false
2016-10-05 02:16:02 +05:00
// Set window position on restore. Window loosing it on
// multimonitor configurations.
m . window . Move ( m . window_pos_x , m . window_pos_y )
2016-10-05 01:03:46 +05:00
} else {
m . window . Hide ( )
m . hidden = true
}
}
func ( m * MainWindow ) showTrayMenu ( cbx * glib . CallbackContext ) {
m . tray_menu . Popup ( nil , nil , gtk . StatusIconPositionMenu , m . tray_icon , uint ( cbx . Args ( 0 ) ) , uint32 ( cbx . Args ( 1 ) ) )
}
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) unlockInterface ( ) {
m . launch_button . SetSensitive ( true )
m . statusbar . Push ( m . statusbar_context_id , "URTrator is ready." )
}
2016-10-06 13:55:03 +05:00
// Triggered when "Update all servers" button is clicked.
2016-10-04 15:42:36 +05:00
func ( m * MainWindow ) UpdateServers ( ) {
m . statusbar . Push ( m . statusbar_context_id , "Updating servers..." )
current_tab := m . tab_widget . GetTabLabelText ( m . tab_widget . GetNthPage ( m . tab_widget . GetCurrentPage ( ) ) )
fmt . Println ( "Updating servers on tab '" + current_tab + "'..." )
2016-10-06 13:55:03 +05:00
2016-10-04 23:39:32 +05:00
if strings . Contains ( current_tab , "Servers" ) {
2016-10-06 13:55:03 +05:00
go ctx . Requester . UpdateAllServers ( )
2016-10-04 23:39:32 +05:00
} else if strings . Contains ( current_tab , "Favorites" ) {
2016-10-06 13:55:03 +05:00
go ctx . Requester . UpdateFavoriteServers ( )
2016-10-04 15:42:36 +05:00
}
}