52 Commits

Author SHA1 Message Date
32c6297765 Fixed game version combobox not selecting proper version on profile editing. 2016-11-25 04:49:19 +05:00
190f76eb82 Fixed profiles editing crashing application if profile wasn't saved in database. 2016-11-25 04:44:46 +05:00
5c295f08ab Prettify options window. 2016-11-25 04:31:45 +05:00
061bfcaeb9 UI design updates.
Got rid of visible separators, replaced them with HBox and VBox.

Reworked profile add/edit window, now it looks prettier and
organic.
2016-11-25 04:11:44 +05:00
5ca8299714 Show selected server's information on active tab changing.
URTrator will now show information about selected server on
activated tab. E.g. when you switched from "Servers" to
"Favorites", sidebar informational widget will be updated with
server's data for selected server on "Favorites" tab.
2016-11-25 03:12:05 +05:00
9c8aa1cc34 Preparing to 0.1.0 release. 2016-11-23 10:13:01 +05:00
5fcbd8536c Update selected server's info in sidebar after updating server's data. 2016-11-23 10:10:11 +05:00
fb7a471aec Shw message in toolbar when updating selected server's data. 2016-11-23 10:02:58 +05:00
8c9d948a52 Fixed tooltip text for "Update all servers" button. 2016-11-23 09:53:09 +05:00
8b44d194b5 Hide private servers checkboxes. 2016-11-23 09:49:39 +05:00
3b0a93e63a By default srting players by name as well as cvars in sidebar. 2016-11-23 07:51:41 +05:00
b85ba0470c Fixed rendering of logo on launch button.
Also, made this logo smaller to beautify interface.
2016-11-23 07:42:58 +05:00
f127fdc9f4 Fixed players counting on servers update. 2016-11-23 07:41:14 +05:00
5ef54d943e Removed unneded "Additional information" button. 2016-11-23 07:35:10 +05:00
9e6cdfc869 Increased max ping requests to servers. Fixes #3. 2016-11-22 18:50:56 +05:00
bfd3f884b3 Automatic profile selection. Fixes #14.
Automatically select first added profile on every profiles list
update. Yes, quick hack. In future we will autodetect profiles
based on server version, and profiles combobox will only be
needed for quick connect.
2016-11-22 18:33:53 +05:00
7e6b698c1c Proper sorting by servers names. Fixing #12. 2016-11-22 18:23:43 +05:00
4516abb916 go-gtk fixed default sorting, so unlock it for all OSes. 2016-11-22 17:14:02 +05:00
e731653652 Fix for previous commit, wrong sorting call was masked on Windows. 2016-11-22 14:37:11 +05:00
e16d6fa878 Disabling setting default sort column on windows until it will
be fixed in go-gtk (https://github.com/mattn/go-gtk/issues/291).
2016-11-22 14:25:16 +05:00
89298d894b Hello, 0.1.0-beta5! 2016-11-22 12:02:12 +05:00
88b9b31f6a Updated README about required Go version. 2016-11-22 11:56:48 +05:00
1fe49871f1 Proper sorting by server name. 2016-11-22 11:53:54 +05:00
4f5bf90dbe Set 4.3.1 as default version in profiles dialog. 2016-11-22 11:38:48 +05:00
25bebf87df Re-enabled sorting for servers lists and hide offline checkboxes default value.
Re-enabled sorting for servers lists, which forces to use devel
version of Go. 1.7.3 have problems with GC and URTrator will
segfault.

Hide offline checkboxes for all servers and favorites now
checked by default.
2016-11-22 11:36:17 +05:00
c4ed421106 Temporary disable sorting in servers list until fixed in go-gtk. 2016-11-22 10:51:48 +05:00
0c0fcd5b24 First "sort by server name" implementation and button name fix.
First implementation of "Sort by server name" algo. Shitty, kinda
unreliable, but better than GTK's default.

Added Colorizer.Clear() func, which clears passed data from
colorcodes.

Fixed name on "Add to favorites" button.

Still cannot be successfully launched until some race conditions
will be fixed in go-gtk.
2016-11-22 09:48:19 +05:00
4d466a9d2b Servers sorting by ping and players count.
For now only on "Servers" tab, and REQUIRES custom go-gtk build
(see https://github.com/mattn/go-gtk/issues/290#issuecomment-262127956)
2016-11-22 07:36:27 +05:00
2cdee94efe Clipboard monitoring is here, so updating README. 2016-11-22 05:42:15 +05:00
2b1caac8f6 Fixed caching servers not updating profile to use in database. 2016-11-22 05:03:30 +05:00
862b336556 Windows-related build script and game launching fixes.
Fixed Windows build script to install gcc in MSYS2, which is
needed for some dependencies.

Fixed game launching on Windows, now ioq3 will search for game
data in proper directory.
2016-11-22 04:56:18 +05:00
90f888bdfb Toolbar icons is now (temporary) hardcoded. 2016-11-22 03:30:58 +05:00
a132ee0f7b Fixed icons loading and changed icons from svg to png. 2016-11-22 02:55:48 +05:00
8b06dff083 Forcing icons for servers lists. 2016-11-22 02:15:07 +05:00
ea8bdde224 Temporary disabled all modals on Linuxes due to mattn/go-gtk#289. 2016-11-22 01:10:31 +05:00
6495e90737 gdk_threads are deprecated, as well as glib_threads. 2016-11-21 03:32:22 +05:00
3e89df1009 Missing return definition for functions in prev commit. 2016-11-21 02:52:26 +05:00
c00ff2cea5 Check window position and size and hpane size in glib thread. 2016-11-21 02:49:05 +05:00
93d2bb7fc1 Reverting moving LockOSThread to main module. 2016-11-21 02:43:05 +05:00
0392f510f5 Moved runtime.LockOSThread() to urtrator.go. 2016-11-21 01:33:04 +05:00
57a5f7cb4c Bumping to beta4. Not tagging, as Windows fix is coming. 2016-11-21 01:19:06 +05:00
8a3f2a9a2e Stop updating servers if we're failed to connect to master server. 2016-11-04 19:37:50 +05:00
4e9a6ae970 GLib threads initialization. 2016-11-04 18:46:07 +05:00
7f80f99529 GTK actions fix WIP and icon size fix.
Again, possibly fixed GTK actions calls from other goroutines
by wrapping events calls in glib.IdleAdd(). This should prevent
executing more than one event simultaneously, which will also
prevent crashes.

Fixed icons sizing for couple of icons.
2016-11-04 18:28:52 +05:00
b1d38bbdcf Possibly solved crashes on GTK's actions. 2016-11-02 22:28:15 +05:00
e487b8521c Fixed clipboard watcher not watching after copying server's creds. 2016-10-13 02:40:27 +05:00
e12c76e424 Saving passwords for favorited private servers. 2016-10-12 23:26:43 +05:00
640020d23f Fixed server name putting into status label.
Some server's names (like Turnpike City) was tructating without
preserving Pango markup integrity. This commit fixes it.
2016-10-12 22:18:36 +05:00
a64c842e19 Messageboxes fixes and copy server's creds to clipboard.
Fixed all messageboxes that can use struct object as self name
(e.g. m for messageboxes in mainwindow). Should help to avoid some
errors.

Added possibility to copy server's credentials into clipboard
for ease of sharing.
2016-10-12 19:23:10 +05:00
1330699f41 Favorites servers and profiles fix.
If favorite server for some reason have no profile defined - pop up
an error message and do nothing.
2016-10-11 20:07:10 +05:00
a97b041679 Improved clipboard watching mechanism.
And it will look only within first string in clipboard.
2016-10-11 19:49:02 +05:00
68bd842c1f First variant of clipboard watching. 2016-10-11 13:14:08 +05:00
21 changed files with 911 additions and 227 deletions

View File

@@ -2,6 +2,8 @@
[![Join the chat at https://gitter.im/urtrator/Lobby](https://badges.gitter.im/urtrator/Lobby.svg)](https://gitter.im/urtrator/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/urtrator/Lobby](https://badges.gitter.im/urtrator/Lobby.svg)](https://gitter.im/urtrator/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Chat on Matrix: #gitter_urtrator=2Flobby:matrix.org (Gitter bridge).
![Main Window](/doc/screenshots/0.1-main_window.png) ![Main Window](/doc/screenshots/0.1-main_window.png)
URTrator is a desktop application that should (eventually) replace URTrator is a desktop application that should (eventually) replace
@@ -25,6 +27,7 @@ the game.
* Favorites servers. * Favorites servers.
* Updating single server. * Updating single server.
* Showing information about servers (like in UrT Connector). * Showing information about servers (like in UrT Connector).
* Clipboard monitoring.
Planning: Planning:
@@ -35,11 +38,15 @@ Planning:
* All kinds of notifications. * All kinds of notifications.
* Extended profile editor, so every profile could have own configuration * Extended profile editor, so every profile could have own configuration
files, etc. files, etc.
* Clipboard monitoring.
* ...maybe more :) * ...maybe more :)
# Installation # Installation
## Precautions
Due to Go's GC bugs, it is required to use devel version of Go
compiler!
## Release ## Release
You don't need to install anything, thanks to Go's. You don't need to install anything, thanks to Go's.

View File

@@ -65,6 +65,7 @@ func (c *Cache) FlushServers(data map[string]string) {
new_servers[mapping_item_name].IsPrivate = s.Server.IsPrivate new_servers[mapping_item_name].IsPrivate = s.Server.IsPrivate
new_servers[mapping_item_name].Favorite = s.Server.Favorite new_servers[mapping_item_name].Favorite = s.Server.Favorite
new_servers[mapping_item_name].ProfileToUse = s.Server.ProfileToUse new_servers[mapping_item_name].ProfileToUse = s.Server.ProfileToUse
new_servers[mapping_item_name].Password = s.Server.Password
} else { } else {
cached_servers[mapping_item_name].Ip = s.Server.Ip cached_servers[mapping_item_name].Ip = s.Server.Ip
cached_servers[mapping_item_name].Port = s.Server.Port cached_servers[mapping_item_name].Port = s.Server.Port
@@ -80,6 +81,7 @@ func (c *Cache) FlushServers(data map[string]string) {
cached_servers[mapping_item_name].IsPrivate = s.Server.IsPrivate cached_servers[mapping_item_name].IsPrivate = s.Server.IsPrivate
cached_servers[mapping_item_name].Favorite = s.Server.Favorite cached_servers[mapping_item_name].Favorite = s.Server.Favorite
cached_servers[mapping_item_name].ProfileToUse = s.Server.ProfileToUse cached_servers[mapping_item_name].ProfileToUse = s.Server.ProfileToUse
cached_servers[mapping_item_name].Password = s.Server.Password
} }
} }
@@ -88,12 +90,12 @@ func (c *Cache) FlushServers(data map[string]string) {
fmt.Println("Adding new servers...") fmt.Println("Adding new servers...")
if len(new_servers) > 0 { if len(new_servers) > 0 {
for _, srv := range new_servers { for _, srv := range new_servers {
tx.NamedExec("INSERT INTO servers (ip, port, name, ping, players, maxplayers, gamemode, map, version, extended_config, players_info, is_private, favorite) VALUES (:ip, :port, :name, :ping, :players, :maxplayers, :gamemode, :map, :version, :extended_config, :players_info, :is_private, :favorite)", srv) tx.NamedExec("INSERT INTO servers (ip, port, name, ping, players, maxplayers, gamemode, map, version, extended_config, players_info, is_private, favorite, profile_to_use) VALUES (:ip, :port, :name, :ping, :players, :maxplayers, :gamemode, :map, :version, :extended_config, :players_info, :is_private, :favorite, :profile_to_use)", srv)
} }
} }
fmt.Println("Updating cached servers...") fmt.Println("Updating cached servers...")
for _, srv := range cached_servers { for _, srv := range cached_servers {
_, err := tx.NamedExec("UPDATE servers SET name=:name, players=:players, maxplayers=:maxplayers, gamemode=:gamemode, map=:map, ping=:ping, version=:version, extended_config=:extended_config, favorite=:favorite, password=:password, players_info=:players_info, is_private=:is_private WHERE ip=:ip AND port=:port", &srv) _, err := tx.NamedExec("UPDATE servers SET name=:name, players=:players, maxplayers=:maxplayers, gamemode=:gamemode, map=:map, ping=:ping, version=:version, extended_config=:extended_config, favorite=:favorite, password=:password, players_info=:players_info, is_private=:is_private, profile_to_use=:profile_to_use WHERE ip=:ip AND port=:port", &srv)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
} }

View File

@@ -0,0 +1,114 @@
// 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 clipboardwatcher
import (
// stdlib
"errors"
"fmt"
"strings"
// other
"github.com/mattn/go-gtk/gdk"
"github.com/mattn/go-gtk/gtk"
)
type ClipboardWatcher struct {
// Clipboard.
clipboard *gtk.Clipboard
// PRIMARY clipboard.
prim_clipboard *gtk.Clipboard
// Flags.
// We have just copy connect string to clipboard.
// Used to ignore clipboard data in check*Input()
just_set bool
}
func (cw *ClipboardWatcher) checkInput() {
if !cw.just_set {
text := cw.clipboard.WaitForText()
cw.parseData(text)
} else {
cw.just_set = false
}
}
func (cw *ClipboardWatcher) checkPrimaryInput() {
if !cw.just_set {
text := cw.prim_clipboard.WaitForText()
cw.parseData(text)
} else {
cw.just_set = false
}
}
func (cw *ClipboardWatcher) CopyServerData(server_address string) error {
server, ok := Cache.Servers[server_address]
if !ok {
// ToDo: show message box?
return errors.New("Server wasn't selected")
}
// Composing connection string.
var connect_string string = ""
connect_string += "/connect " + server.Server.Ip + ":" + server.Server.Port
if len(server.Server.Password) >= 1 {
connect_string += ";password " + server.Server.Password
}
fmt.Println("Connect string: ", connect_string)
cw.just_set = true
cw.clipboard.SetText(connect_string)
return nil
}
func (cw *ClipboardWatcher) Initialize() {
fmt.Println("Initializing clipboard watcher...")
cw.just_set = false
cw.clipboard = gtk.NewClipboardGetForDisplay(gdk.DisplayGetDefault(), gdk.SELECTION_CLIPBOARD)
cw.clipboard.Connect("owner-change", cw.checkInput)
cw.prim_clipboard = gtk.NewClipboardGetForDisplay(gdk.DisplayGetDefault(), gdk.SELECTION_PRIMARY)
cw.prim_clipboard.Connect("owner-change", cw.checkPrimaryInput)
}
func (cw *ClipboardWatcher) parseData(data string) {
// We should check only first string.
data = strings.Split(data, "\n")[0]
// Checking if we have connection string here.
if strings.Contains(data, "ct ") {
fmt.Println("Connection string detected!")
var server string = ""
var password string = ""
conn_string := strings.Split(data, ";")
if len(conn_string) > 0 {
srv_string := strings.Split(data, ";")[0]
srv_splitted := strings.Split(srv_string, "ct ")
if len(srv_splitted) > 1 {
server_raw := strings.Split(srv_splitted[1], " ")[0]
// Get rid of spaces.
server = strings.TrimSpace(server_raw)
}
}
if len(conn_string) > 1 && strings.Contains(data, "password") {
pw_string := strings.Split(data, ";")[1]
pw_splitted := strings.Split(pw_string, "password ")
if len(pw_splitted) > 1 {
password_raw := strings.Split(pw_splitted[1], " ")[0]
// Get rid of spaces.
password = strings.TrimSpace(password_raw)
}
}
Eventer.LaunchEvent("setQuickConnectDetails", map[string]string{"server": server, "password": password})
}
}

View File

@@ -0,0 +1,28 @@
// 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 clipboardwatcher
import(
// local
"github.com/pztrn/urtrator/cache"
"github.com/pztrn/urtrator/eventer"
)
var (
Cache *cache.Cache
Eventer *eventer.Eventer
)
func New(c *cache.Cache, e *eventer.Eventer) *ClipboardWatcher {
Cache = c
Eventer = e
cw := ClipboardWatcher{}
return &cw
}

View File

@@ -21,8 +21,28 @@ type Colorizer struct {
colors map[string]string colors map[string]string
} }
func (c *Colorizer) ClearFromMarkup(data string) string {
var result string = ""
data = html.EscapeString(data)
data_splitted := strings.Split(data, ">")
if len(data_splitted) > 1 {
for item := range data_splitted {
if len(data_splitted[item]) > 0 {
result += strings.Split(data_splitted[item], "<")[0]
}
}
} else {
result = data_splitted[0]
}
return result
}
func (c *Colorizer) Fix(data string) string { func (c *Colorizer) Fix(data string) string {
result := "" var result string = ""
data = html.EscapeString(data) data = html.EscapeString(data)

View File

@@ -10,5 +10,5 @@
package common package common
const ( const (
URTRATOR_VERSION = "0.1-beta3" URTRATOR_VERSION = "0.1.0"
) )

View File

@@ -0,0 +1,16 @@
// 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 common
var SERVER_OFFLINE = `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`
var SERVER_ONLINE = `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`
var SERVER_PRIVATE = `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`
var SERVER_PUBLIC = `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
`

22
common/toolbar_icons.go Normal file
View File

@@ -0,0 +1,22 @@
// 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 common
var ADD_TO_FAVORITES = `iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAK6SURBVFiFxZexaxRBGMXft7t6B4pR8CSKovbBQsTOwkrRMoVgm/wDVgpiZSUW+Qe0F9HCMkHsAjYiSMAUijZyIYdgipCbnW++Z3F7l93bu0v2NJuBZZZlZ+bHmzdvZoQkDrNEhzo6gKTKzyJyAkAT48ENgCO5dSAAAJoPX15ZI6w1EhBR59nCl7kqHVYFiIKF1v1bj5DETQAAAYAGH3bwauV5CxWntSoAjIYoSvDz9ypEBIAAIM7NXIXRqnY3BYARgQpjgEByYAqz6itqCgUImsKoEOYAzMOmWNLTKZABINOAAAJ9XQpYT26Goe9apwc8zLTwPdg/KpCFTANAPBGAhJpHGAYIux4QkdlJXSAXVnkFmotLl9cAjgyZfonj2AXVRgjFKVBTJHHsFpcutSe1B6Tz4sGPQVjlASKjteZvLuBI0sg+sfdI7h1oOL8N1UyBzIUu3cbdG/capMFoGNRmMAaYGdQc3n9cLoRVwQNmRIQY678+IJIIIjLyATCo+2Xjzzr6OyvJkc/JY+dLPikCkNCgCCGAwtGDC3YDKM/AfkWAoyFUy1lRAAhmPYONARgMnocoMGSDj4HQ4BGsuFRLCgTtAZhYWYHhwScpgCkUMCO66TYunr6GvvnYNx85eFdz2Nz6VlDh1PELiCRGyYQ0mAUYDanvTvSAkei8W30zcRkmSeJuX79TWoY0YvnTilPVxpimmWjSQS8LSgDu9eP2HPbYz+efnml7TaGqBQW8pkjTtPH2yebZSe2zwV0JYD/HKBGZtWwuhxXwufklubFXXyWA/RYzwqsrK+BdfbvhKAV6Dq9pN/Q+80AuDVNN6zsROU3hNRRyIPVpfSci5z3SEAo51PW1KWDouh00j86ABJiFTTft1uIBo6Hz+fvXMReTYsjsp0iVy6mIzKB3avpvV7NKAAdRDv12/Bc1PDWiO2VdhgAAAABJRU5ErkJggg==`
var COPY_CREDENTIALS = `iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAB11BMVEUAAAD///////+AgICAj4CJiYDDw8OQk42PkYyNkIqFh4KHioWHiYWJi4WJi4eLjYfDw8PFxcPFxcXHx8XHx8fJycfJycnp6enr6+nr6+vt7evt7e3v7+3v7+/x8e/x8fHz8/P19fX39/f5+fn7+/v9/f3///+IiYSQkY2QkY2GioWLjoiZm5aFioSGioSIi4eIi4aHioWIioWIi4aLjomGioSHiYSHioWIioSIioWIi4WJi4aJjIiJjYeLjomMjoqMjouMkImMkIqNkIuSlpCTlpGVlpWVmJOYm5eYnJeanJqbm5ubnpqdnZ2enp6fn5+kpKSmpqamqaWoqKioqaipqamwsa+xsbGzs7O0tLS5ubm6urq9vr2+vr7BwcHCwsLDw8LDw8PExMPExMTFxcTFxcXGxsXGxsbGx8XGx8bHx8bHx8fIyMfIyMjNzc3R0dHm5ubn5+fo6OXo6Ofo6Ojp6ejp6enp6ujq6unq6urr6+rr6+vs7Ovs7Ozt7evt7ezt7e3u7u3u7u7v7+3v7+7v7+/w8O/w8PDx8fDx8fHy8vLz8/P09PL09PT19fX29vX29vb39/X39/f4+Pf4+Pj5+fn6+vr7+/r7+/v8/Pz9/f3+/v7///9tYCWGAAAANXRSTlMAAQIMEBoiXF1eYmZ/f39/f39/f39/f39/f39/f39/f39/f39/f3+ct77C29zo7fD4+v7+/rdyDXMAAAABYktHRJxxvMInAAACOklEQVRIx51VQW7bMBAc7i5jt0AudRK/off+qO/qN/qWIO61SFEgAewmliWLnB5IS5SlKnF1kUDO7M7MEhRw4eOwNDezz2M9XDAs1rMVH0cE4DCDX54vyKUeLibY7C4vI/BCSZzk2L+qcd4Dc7Tr63PAqvgOu/uuQ5729dfZEL51HVw3oN8z+Ls5D3ifh/cPzr0NjSUhe5gRRo7nwPnykUMPb8ARzjzQ9ZTH8dTJNcoOWKTlqYwjENiZsK5CgbwdqyGYYrIEi4eyri/wikgBiVhKIhwKE48D8UAgbMWCQLhFqZ2FGoIgGDI+S2I3l8JDjp4EQTlBsgceSooHEKEIAhKIxFlKuQyKOSRESN3h7niiWNpzgwuLffSZQIIpJiu6nd5xlaInQDACdETIKRjAjOtfvoieSqaesScUxx0RiL/6OHEyx5s0Tzs77gACWg6iTtrEL9CbTlsiKfpb5rNzUkM4/9FbR4gZLgIQpKXoJXlWRHjzXlWypB7vUJbuzIr33quayx0+EICDc3BdNr0awi+8iZqqEDAcn8qrikkNQeTokxr1KrIPgKEqZ1wzluWTGjM1UYmbzcT/gRyqsaWJft9i6UHnmjEhJPxAjemXK1b7pvm5me5QqhG9EhU+B7b1rqrGhMhejfempipOXxiPzZ/nDceENquRRVIjqm3dNq5pttv7MGU6FGrMVFjVVYjt/ql6aKf+ouGGEeKcOFOnTo6Hev9S7193D6+T1/znT6P7HYi6r34E/OfzFzi+itJ7Ai74AAAAAElFTkSuQmCC`
var EDIT_FAVORITE = `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`
var REFRESH_ALL_SERVERS = `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`
var REFRESH_ONE_SERVER = `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`
var REMOVE_FAVORITE = `iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFFSURBVFiF7ZYxbsMwDEU/1Qbp1E6ekjno0Gv0ELllpiw9RYAORYCeIFMHA7Ec8neQZUtuk06GO8gA8anpP5IybSGJOR83q3sBKAAFoAD8B4D7mIjII4AlgLuJPQ1AQ/IrAwDw8LbZvAtZTelOkdPr8fgSzymAo2r1vN3CLZcQkT4AADF3w9T6zxiZhxloBqiCXU4zmPf42O0qJKNPAWCqIID6cIA4F0JkUJEBJMYIgomSDOadLlYrmGrWkQyAZlDvoW2bA4yM065kAEAGMAZxTRM6cw3AVKHe4xIB0jGkOjZPIBh1DEBCmuZ2B0wVl/M5dCAx/a3y3l5kqP5KJ2Lu/gKgGdq6xmK9zgx+1NqZSDTqVNJLCATtzkaireubIzA1O33u95O/hgi7IBQRKxCRJ4RFNPV2zBaRlJ/SAlAACkABmBvgG+Wf86x1nhNMAAAAAElFTkSuQmCC`

View File

@@ -16,6 +16,7 @@ import (
// local // local
"github.com/pztrn/urtrator/cache" "github.com/pztrn/urtrator/cache"
"github.com/pztrn/urtrator/clipboardwatcher"
"github.com/pztrn/urtrator/colorizer" "github.com/pztrn/urtrator/colorizer"
"github.com/pztrn/urtrator/configuration" "github.com/pztrn/urtrator/configuration"
"github.com/pztrn/urtrator/database" "github.com/pztrn/urtrator/database"
@@ -30,6 +31,8 @@ import (
type Context struct { type Context struct {
// Caching. // Caching.
Cache *cache.Cache Cache *cache.Cache
// Clipboard watcher.
Clipboard *clipboardwatcher.ClipboardWatcher
// Colors parser and prettifier. // Colors parser and prettifier.
Colorizer *colorizer.Colorizer Colorizer *colorizer.Colorizer
// Configuration. // Configuration.
@@ -65,6 +68,11 @@ func (ctx *Context) initializeCache() {
ctx.Cache.Initialize() ctx.Cache.Initialize()
} }
func (ctx *Context) InitializeClipboardWatcher() {
ctx.Clipboard = clipboardwatcher.New(ctx.Cache, ctx.Eventer)
ctx.Clipboard.Initialize()
}
func (ctx *Context) initializeColorizer() { func (ctx *Context) initializeColorizer() {
ctx.Colorizer = colorizer.New() ctx.Colorizer = colorizer.New()
ctx.Colorizer.Initialize() ctx.Colorizer.Initialize()

View File

@@ -36,7 +36,7 @@ check_msys() {
# Check if we have neccessary MSYS packages installed. # Check if we have neccessary MSYS packages installed.
check_msys_packages() { check_msys_packages() {
echo "* Installing neccessary MSYS2 packages..." echo "* Installing neccessary MSYS2 packages..."
pacman -S ${MINGW_PACKAGE_PREFIX}-tools-git ${MINGW_PACKAGE_PREFIX}-gtk2 ${MINGW_PACKAGE_PREFIX}-pkg-config ${MINGW_PACKAGE_PREFIX}-gtk-engines ${MINGW_PACKAGE_PREFIX}-gtk-engine-murrine --noconfirm --needed pacman -S ${MINGW_PACKAGE_PREFIX}-tools-git ${MINGW_PACKAGE_PREFIX}-gtk2 ${MINGW_PACKAGE_PREFIX}-pkg-config ${MINGW_PACKAGE_PREFIX}-gtk-engines ${MINGW_PACKAGE_PREFIX}-gtk-engine-murrine ${MINGW_PACKAGE_PREFIX}-gcc --noconfirm --needed
} }
# Build URTrator # Build URTrator

View File

@@ -13,6 +13,10 @@ import (
crand "crypto/rand" crand "crypto/rand"
"errors" "errors"
"fmt" "fmt"
//"reflect"
// github
"github.com/mattn/go-gtk/glib"
) )
type Eventer struct { type Eventer struct {
@@ -44,11 +48,19 @@ func (e *Eventer) LaunchEvent(event string, data map[string]string) error {
if !ok { if !ok {
return errors.New("Event " + event + " not found!") return errors.New("Event " + event + " not found!")
} }
fmt.Println("Launching event " + event)
for _, val := range e.events[event] { fmt.Println("Launching event " + event)
val(data) glib.IdleAdd(func() bool {
} e.reallyLaunchEvent(event, data)
return false
})
return nil return nil
} }
func (e *Eventer) reallyLaunchEvent(event string, data map[string]string) {
fmt.Println("Really launching event " + event + "...")
for _, val := range e.events[event] {
val(data)
}
}

View File

@@ -15,6 +15,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
@@ -34,13 +35,17 @@ type Launcher struct {
func (l *Launcher) CheckForLaunchedUrbanTerror() error { func (l *Launcher) CheckForLaunchedUrbanTerror() error {
if l.launched { if l.launched {
mbox_string := "Game is launched.\n\nCannot quit, because game is launched.\nQuit Urban Terror to exit URTrator!" // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(nil, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Response(func() { if runtime.GOOS != "linux" {
m.Destroy() mbox_string := "Game is launched.\n\nCannot quit, because game is launched.\nQuit Urban Terror to exit URTrator!"
}) m := gtk.NewMessageDialog(nil, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Run() m.Response(func() {
return errors.New("User didn't select valid profile, mismatch with server's version.") m.Destroy()
})
m.Run()
return errors.New("User didn't select valid profile, mismatch with server's version.")
}
} }
return nil return nil
@@ -110,6 +115,12 @@ func (l *Launcher) Launch(server_profile *datamodels.Server, user_profile *datam
go func() { go func() {
go func() { go func() {
cmd := exec.Command(launch_bin, launch_params...) cmd := exec.Command(launch_bin, launch_params...)
// This workaround is required on Windows, otherwise ioq3
// will not find game data.
if runtime.GOOS == "windows" {
dir := filepath.Dir(launch_bin)
cmd.Dir = dir
}
out, err1 := cmd.Output() out, err1 := cmd.Output()
if err1 != nil { if err1 != nil {
fmt.Println("Launch error: " + err1.Error()) fmt.Println("Launch error: " + err1.Error())

View File

@@ -14,7 +14,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
//"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@@ -34,7 +34,8 @@ type Pooler struct {
func (p *Pooler) Initialize() { func (p *Pooler) Initialize() {
fmt.Println("Initializing requester goroutine pooler...") fmt.Println("Initializing requester goroutine pooler...")
// ToDo: figure out how to make this work nice. // ToDo: figure out how to make this work nice.
p.maxrequests = 100 p.maxrequests = 200
_ = runtime.GOMAXPROCS(runtime.NumCPU() * 4)
p.pp = "\377\377\377\377" p.pp = "\377\377\377\377"
fmt.Println("Pooler initialized") fmt.Println("Pooler initialized")
} }
@@ -237,7 +238,9 @@ func (p *Pooler) UpdateSpecificServer(server *datamodels.Server) error {
if len(players) == 1 && len(players[0]) > 255 { if len(players) == 1 && len(players[0]) > 255 {
server.Players = "0" server.Players = "0"
} else { } else {
server.Players = strconv.Itoa(len(players)) // Looks like we have last element to be empty, due to
// strings.Split() call before.
server.Players = strconv.Itoa(len(players) - 1)
} }
server.PlayersInfo = strings.Join(received_lines[2:], "\\") server.PlayersInfo = strings.Join(received_lines[2:], "\\")
} }

View File

@@ -12,6 +12,7 @@ package requester
import ( import (
// stdlib // stdlib
"bytes" "bytes"
"errors"
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
@@ -46,11 +47,13 @@ func (r *Requester) Initialize() {
// Gets all available servers from master server. // Gets all available servers from master server.
// This isn't in pooler, because it have no need to be pooled. // This isn't in pooler, because it have no need to be pooled.
func (r *Requester) getServers() { func (r *Requester) getServers() error {
// IP addresses we will compose to return. // IP addresses we will compose to return.
conn, err1 := net.Dial("udp", r.master_server + ":" + r.master_server_port) conn, err1 := net.Dial("udp", r.master_server + ":" + r.master_server_port)
if err1 != nil { if err1 != nil {
fmt.Println("Error dialing to master server!") fmt.Println("Error dialing to master server!")
Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "Failed to connect to master server!"})
return errors.New("Failed to connect to master server!")
} }
defer conn.Close() defer conn.Close()
@@ -118,6 +121,8 @@ func (r *Requester) getServers() {
Cache.Servers[addr].Server.Port = port Cache.Servers[addr].Server.Port = port
} }
} }
return nil
} }
// Updates information about all available servers from master server and // Updates information about all available servers from master server and

View File

@@ -14,6 +14,7 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"runtime"
"strings" "strings"
// Local // Local
@@ -152,7 +153,7 @@ func (f *FavoriteDialog) initializeWindow() {
srv_addr_hbox.PackStart(f.server_address, true, true, 5) srv_addr_hbox.PackStart(f.server_address, true, true, 5)
srv_addr_update_btn := gtk.NewButton() srv_addr_update_btn := gtk.NewButton()
srv_addr_update_btn.SetTooltipText("Update server information") srv_addr_update_btn.SetTooltipText("Update server information")
srv_addr_update_btn_image := gtk.NewImageFromStock(gtk.STOCK_REDO, 24) srv_addr_update_btn_image := gtk.NewImageFromStock(gtk.STOCK_REDO, gtk.ICON_SIZE_SMALL_TOOLBAR)
srv_addr_update_btn.SetImage(srv_addr_update_btn_image) srv_addr_update_btn.SetImage(srv_addr_update_btn_image)
srv_addr_update_btn.Clicked(f.updateServerInfo) srv_addr_update_btn.Clicked(f.updateServerInfo)
srv_addr_hbox.PackStart(srv_addr_update_btn, false, true, 5) srv_addr_hbox.PackStart(srv_addr_update_btn, false, true, 5)
@@ -205,12 +206,16 @@ func (f *FavoriteDialog) saveFavorite() error {
//ctx.Requester.Pooler.UpdateSpecificServer(f.server) //ctx.Requester.Pooler.UpdateSpecificServer(f.server)
if len(f.server_address.GetText()) == 0 { if len(f.server_address.GetText()) == 0 {
mbox_string := "Server address is empty.\n\nServers without address cannot be added." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(f.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Response(func() { if runtime.GOOS != "linux" {
m.Destroy() mbox_string := "Server address is empty.\n\nServers without address cannot be added."
}) m := gtk.NewMessageDialog(f.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Run() m.Response(func() {
m.Destroy()
})
m.Run()
}
return errors.New("No server address specified") return errors.New("No server address specified")
} }
@@ -224,12 +229,16 @@ func (f *FavoriteDialog) saveFavorite() error {
f.server.Port = port f.server.Port = port
if len(f.profile.GetActiveText()) == 0 { if len(f.profile.GetActiveText()) == 0 {
mbox_string := "Profile wasn't selected.\n\nPlease, select valid profile for this server.\nIf you haven't add profiles yet - you can do it\nin options on \"Urban Terror\" tab." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(f.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Response(func() { if runtime.GOOS != "linux" {
m.Destroy() mbox_string := "Profile wasn't selected.\n\nPlease, select valid profile for this server.\nIf you haven't add profiles yet - you can do it\nin options on \"Urban Terror\" tab."
}) m := gtk.NewMessageDialog(f.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Run() m.Response(func() {
m.Destroy()
})
m.Run()
}
return errors.New("No game profile specified") return errors.New("No game profile specified")
} }

View File

@@ -13,6 +13,7 @@ import (
// stdlib // stdlib
"fmt" "fmt"
"runtime" "runtime"
"sort"
"strconv" "strconv"
"strings" "strings"
@@ -59,8 +60,12 @@ type MainWindow struct {
profiles *gtk.ComboBoxText profiles *gtk.ComboBoxText
// Checkbox for hiding/showing offline servers in 'Servers' tab list. // Checkbox for hiding/showing offline servers in 'Servers' tab list.
all_servers_hide_offline *gtk.CheckButton all_servers_hide_offline *gtk.CheckButton
// Checkbox for hiding/showing passworded servers in 'Servers' tab list.
all_servers_hide_private *gtk.CheckButton
// Checkbox for hiding/showing offline servers in 'Favorites' tab list. // Checkbox for hiding/showing offline servers in 'Favorites' tab list.
fav_servers_hide_offline *gtk.CheckButton fav_servers_hide_offline *gtk.CheckButton
// Checkbox for hiding/showing passworded servers in 'Favorites' tab list.
fav_servers_hide_private *gtk.CheckButton
// Game launch button. // Game launch button.
launch_button *gtk.Button launch_button *gtk.Button
// Server's information. // Server's information.
@@ -121,14 +126,20 @@ type MainWindow struct {
// For online server. // For online server.
server_online_pic *gdkpixbuf.Pixbuf server_online_pic *gdkpixbuf.Pixbuf
// For private (passworded) server. // For private (passworded) server.
server_passworded_pic *gdkpixbuf.Pixbuf server_private_pic *gdkpixbuf.Pixbuf
// For public server // For public server
server_public_pic *gdkpixbuf.Pixbuf server_public_pic *gdkpixbuf.Pixbuf
// Flags. // Flags.
// Application is initialized?
initialized bool
// Window is hidden? // Window is hidden?
hidden bool hidden bool
// Use other's tab information?
// Used when user changed active tab, to show information about
// server which is selected on activated tab.
use_other_servers_tab bool
} }
func (m *MainWindow) addToFavorites() { func (m *MainWindow) addToFavorites() {
@@ -160,17 +171,23 @@ func (m *MainWindow) addToFavorites() {
// Executes when delimiter for two panes is moved, to calculate VALID // Executes when delimiter for two panes is moved, to calculate VALID
// position. // position.
func (m *MainWindow) checkMainPanePosition() { func (m *MainWindow) checkMainPanePosition() {
m.pane_negative_position = m.window_width - m.hpane.GetPosition() glib.IdleAdd(func() bool {
m.pane_negative_position = m.window_width - m.hpane.GetPosition()
return false
})
} }
// Executes when main window is moved or resized. // Executes when main window is moved or resized.
// Also calculating pane delimiter position and set it to avoid // Also calculating pane delimiter position and set it to avoid
// widgets hell :). // widgets hell :).
func (m *MainWindow) checkPositionAndSize() { func (m *MainWindow) checkPositionAndSize() {
m.window.GetPosition(&m.window_pos_x, &m.window_pos_y) glib.IdleAdd(func() bool {
m.window.GetSize(&m.window_width, &m.window_height) 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) m.hpane.SetPosition(m.window_width - m.pane_negative_position)
return false
})
} }
// Executes on URTrator shutdown. // Executes on URTrator shutdown.
@@ -202,6 +219,13 @@ func (m *MainWindow) Close() {
ctx.Close() ctx.Close()
} }
func (m *MainWindow) copyServerCredentialsToClipboard() {
fmt.Println("Copying server's credentials to clipboard...")
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
server_address := m.getIpFromServersList(current_tab)
ctx.Clipboard.CopyServerData(server_address)
}
// Deleting server from favorites. // Deleting server from favorites.
func (m *MainWindow) deleteFromFavorites() { func (m *MainWindow) deleteFromFavorites() {
fmt.Println("Removing server from favorites...") fmt.Println("Removing server from favorites...")
@@ -221,12 +245,18 @@ func (m *MainWindow) deleteFromFavorites() {
} }
if not_favorited { if not_favorited {
mbox_string := "Cannot delete server from favorites.\n\nServer isn't favorited." // Temporary disable all these modals on Linux.
d := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
d.Response(func() { if runtime.GOOS != "linux" {
d.Destroy() 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.Run() d.Response(func() {
d.Destroy()
})
d.Run()
} else {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Server isn't favorited</span></markup>"})
}
} }
ctx.Eventer.LaunchEvent("loadFavoriteServers", map[string]string{}) ctx.Eventer.LaunchEvent("loadFavoriteServers", map[string]string{})
@@ -237,19 +267,23 @@ func (m *MainWindow) deleteFromFavorites() {
func (m *MainWindow) dropDatabasesData() { func (m *MainWindow) dropDatabasesData() {
fmt.Println("Dropping database data...") fmt.Println("Dropping database data...")
var will_continue bool = false 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?" // Temporary disable all these modals on Linux.
d := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
d.Connect("response", func(resp *glib.CallbackContext) { if runtime.GOOS != "linux" {
if resp.Args(0) == 4294967287 { 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?"
will_continue = false d := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, mbox_string)
} else { d.Connect("response", func(resp *glib.CallbackContext) {
will_continue = true if resp.Args(0) == 4294967287 {
} will_continue = false
}) } else {
d.Response(func() { will_continue = true
d.Destroy() }
}) d.Destroy()
d.Run() })
d.Run()
} else {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Remove ~/.config/urtrator/database.sqlite3 manually!</span></markup>"})
}
if will_continue { if will_continue {
ctx.Database.Db.MustExec("DELETE FROM servers") ctx.Database.Db.MustExec("DELETE FROM servers")
@@ -287,6 +321,18 @@ func (m *MainWindow) hideOfflineAllServers() {
ctx.Eventer.LaunchEvent("loadAllServers", map[string]string{}) ctx.Eventer.LaunchEvent("loadAllServers", map[string]string{})
} }
// Executes when "Hide passworded servers" checkbox changed it's state on
// "Servers" tab.
func (m *MainWindow) hidePrivateAllServers() {
fmt.Println("(Un)Hiding private servers in 'Servers' tab...")
if m.all_servers_hide_private.GetActive() {
ctx.Cfg.Cfg["/serverslist/all_servers/hide_private"] = "1"
} else {
ctx.Cfg.Cfg["/serverslist/all_servers/hide_private"] = "0"
}
ctx.Eventer.LaunchEvent("loadAllServers", map[string]string{})
}
// Executes when "Hide offline servers" checkbox changed it's state on // Executes when "Hide offline servers" checkbox changed it's state on
// "Favorites" tab. // "Favorites" tab.
func (m *MainWindow) hideOfflineFavoriteServers() { func (m *MainWindow) hideOfflineFavoriteServers() {
@@ -299,9 +345,20 @@ func (m *MainWindow) hideOfflineFavoriteServers() {
ctx.Eventer.LaunchEvent("loadFavoriteServers", map[string]string{}) ctx.Eventer.LaunchEvent("loadFavoriteServers", map[string]string{})
} }
// Executes when "Hide passworded servers" checkbox changed it's state on
// "Favorites" tab.
func (m *MainWindow) hidePrivateFavoriteServers() {
fmt.Println("(Un)Hiding private servers in 'Favorite' tab...")
if m.all_servers_hide_private.GetActive() {
ctx.Cfg.Cfg["/serverslist/favorite/hide_private"] = "1"
} else {
ctx.Cfg.Cfg["/serverslist/favorite/hide_private"] = "0"
}
ctx.Eventer.LaunchEvent("loadFavoriteServers", map[string]string{})
}
func (m *MainWindow) loadAllServers(data map[string]string) { func (m *MainWindow) loadAllServers(data map[string]string) {
fmt.Println("Loading all servers...") fmt.Println("Loading all servers...")
// ToDo: do it without clearing.
for _, server := range ctx.Cache.Servers { for _, server := range ctx.Cache.Servers {
iter := new(gtk.TreeIter) iter := new(gtk.TreeIter)
ping, _ := strconv.Atoi(server.Server.Ping) ping, _ := strconv.Atoi(server.Server.Ping)
@@ -314,7 +371,15 @@ func (m *MainWindow) loadAllServers(data map[string]string) {
} }
if m.all_servers_hide_offline.GetActive() && (server.Server.Players == "" && server.Server.Maxplayers == "" || ping > 9000) { if m.all_servers_hide_offline.GetActive() && (server.Server.Players == "" && server.Server.Maxplayers == "" || ping > 9000) {
if server.AllServersIterInList { if server.AllServersIterInList && server.AllServersIterSet {
m.all_servers_store.Remove(iter)
server.AllServersIterInList = false
}
continue
}
if m.all_servers_hide_private.GetActive() && server.Server.IsPrivate == "1" {
if server.AllServersIterInList && server.AllServersIterSet {
m.all_servers_store.Remove(iter) m.all_servers_store.Remove(iter)
server.AllServersIterInList = false server.AllServersIterInList = false
} }
@@ -336,7 +401,7 @@ func (m *MainWindow) loadAllServers(data map[string]string) {
m.all_servers_store.SetValue(iter, 0, m.server_online_pic.GPixbuf) m.all_servers_store.SetValue(iter, 0, m.server_online_pic.GPixbuf)
} }
if server.Server.IsPrivate == "1" { if server.Server.IsPrivate == "1" {
m.all_servers_store.SetValue(iter, 1, m.server_passworded_pic.GPixbuf) m.all_servers_store.SetValue(iter, 1, m.server_private_pic.GPixbuf)
} else { } else {
m.all_servers_store.SetValue(iter, 1, m.server_public_pic.GPixbuf) m.all_servers_store.SetValue(iter, 1, m.server_public_pic.GPixbuf)
} }
@@ -373,6 +438,14 @@ func (m *MainWindow) loadFavoriteServers(data map[string]string) {
continue continue
} }
if m.fav_servers_hide_private.GetActive() && server.Server.IsPrivate == "1" {
if server.FavServersIterInList && server.FavServersIterSet {
m.fav_servers_store.Remove(iter)
server.FavServersIterInList = false
}
continue
}
// If server on favorites widget, but not favorited (e.g. just // If server on favorites widget, but not favorited (e.g. just
// removed from favorites) - remove it from list. // removed from favorites) - remove it from list.
if server.Server.Favorite != "1" && server.FavServersIterSet && server.FavServersIterInList { if server.Server.Favorite != "1" && server.FavServersIterSet && server.FavServersIterInList {
@@ -401,7 +474,7 @@ func (m *MainWindow) loadFavoriteServers(data map[string]string) {
m.fav_servers_store.SetValue(iter, 0, m.server_online_pic.GPixbuf) m.fav_servers_store.SetValue(iter, 0, m.server_online_pic.GPixbuf)
} }
if server.Server.IsPrivate == "1" { if server.Server.IsPrivate == "1" {
m.fav_servers_store.SetValue(iter, 1, m.server_passworded_pic.GPixbuf) m.fav_servers_store.SetValue(iter, 1, m.server_private_pic.GPixbuf)
} else { } else {
m.fav_servers_store.SetValue(iter, 1, m.server_public_pic.GPixbuf) m.fav_servers_store.SetValue(iter, 1, m.server_public_pic.GPixbuf)
} }
@@ -431,10 +504,46 @@ func (m *MainWindow) loadProfiles(data map[string]string) {
m.old_profiles_count = len(ctx.Cache.Profiles) m.old_profiles_count = len(ctx.Cache.Profiles)
fmt.Println("Added " + strconv.Itoa(m.old_profiles_count) + " profiles") fmt.Println("Added " + strconv.Itoa(m.old_profiles_count) + " profiles")
m.profiles.SetActive(0)
}
func (m *MainWindow) tabChanged() {
if !m.initialized {
return
}
fmt.Println("Active tab changed...")
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
fmt.Println(current_tab)
m.use_other_servers_tab = true
if strings.Contains(current_tab, "Servers") {
m.fav_servers.Emit("cursor-changed")
} else if strings.Contains(current_tab, "Favorites") {
m.all_servers.Emit("cursor-changed")
}
m.use_other_servers_tab = false
} }
func (m *MainWindow) serversUpdateCompleted(data map[string]string) { func (m *MainWindow) serversUpdateCompleted(data map[string]string) {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "Servers updated."}) ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "Servers updated."})
// Trigger "selection-changed" events on currently active tab's
// servers list.
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
if strings.Contains(current_tab, "Servers") {
m.all_servers.Emit("cursor-changed")
} else if strings.Contains(current_tab, "Favorites") {
m.fav_servers.Emit("cursor-changed")
}
}
func (m *MainWindow) setQuickConnectDetails(data map[string]string) {
fmt.Println("Setting quick connect data...")
m.qc_server_address.SetText(data["server"])
m.qc_password.SetText(data["password"])
} }
func (m *MainWindow) setToolbarLabelText(data map[string]string) { func (m *MainWindow) setToolbarLabelText(data map[string]string) {
@@ -459,14 +568,17 @@ func (m *MainWindow) showHide() {
} }
} }
func (m *MainWindow) showServerInformation() {
fmt.Println("Showing server's information...")
}
func (m *MainWindow) showShortServerInformation() { func (m *MainWindow) showShortServerInformation() {
fmt.Println("Server selection changed, updating server's information widget...") fmt.Println("Server selection changed, updating server's information widget...")
m.server_info_store.Clear() m.server_info_store.Clear()
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage())) current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
if m.use_other_servers_tab {
if strings.Contains(current_tab, "Servers") {
current_tab = "Favorites"
} else if strings.Contains(current_tab, "Favorites") {
current_tab = "Servers"
}
}
srv_address := m.getIpFromServersList(current_tab) srv_address := m.getIpFromServersList(current_tab)
// Getting server information from cache. // Getting server information from cache.
@@ -539,12 +651,22 @@ func (m *MainWindow) showShortServerInformation() {
m.server_info_store.Append(iter) m.server_info_store.Append(iter)
m.server_info_store.SetValue(iter, 0, "<markup><span font_weight=\"bold\">PLAYERS</span></markup>") m.server_info_store.SetValue(iter, 0, "<markup><span font_weight=\"bold\">PLAYERS</span></markup>")
for _, value := range parsed_players_info { // Sorting keys of map.
players_map_keys := make([]string, 0, len(parsed_players_info))
for k := range parsed_players_info {
// ToDo: figure out how to do this properly without
// append().
players_map_keys = append(players_map_keys, k)
}
sort.Strings(players_map_keys)
for k := range players_map_keys {
iter = new(gtk.TreeIter) iter = new(gtk.TreeIter)
nick := ctx.Colorizer.Fix(value["nick"]) nick := ctx.Colorizer.Fix(parsed_players_info[players_map_keys[k]]["nick"])
m.server_info_store.Append(iter) m.server_info_store.Append(iter)
m.server_info_store.SetValue(iter, 0, nick) m.server_info_store.SetValue(iter, 0, nick)
m.server_info_store.SetValue(iter, 1, "(frags: " + value["frags"] + " | ping: " + value["ping"] + ")") m.server_info_store.SetValue(iter, 1, "(frags: " + parsed_players_info[players_map_keys[k]]["frags"] + " | ping: " + parsed_players_info[players_map_keys[k]]["ping"] + ")")
} }
// Just a separator. // Just a separator.
@@ -556,11 +678,19 @@ func (m *MainWindow) showShortServerInformation() {
m.server_info_store.Append(iter) m.server_info_store.Append(iter)
m.server_info_store.SetValue(iter, 0, "<markup><span font_weight=\"bold\">OTHER PARAMETERS</span></markup>") m.server_info_store.SetValue(iter, 0, "<markup><span font_weight=\"bold\">OTHER PARAMETERS</span></markup>")
for key, value := range parsed_general_data { // Sort it!
general_data_keys := make([]string, 0, len(parsed_general_data))
for k := range parsed_general_data {
general_data_keys = append(general_data_keys, k)
}
sort.Strings(general_data_keys)
for k := range general_data_keys {
iter = new(gtk.TreeIter) iter = new(gtk.TreeIter)
m.server_info_store.Append(iter) m.server_info_store.Append(iter)
m.server_info_store.SetValue(iter, 0, key) m.server_info_store.SetValue(iter, 0, general_data_keys[k])
m.server_info_store.SetValue(iter, 1, value) m.server_info_store.SetValue(iter, 1, parsed_general_data[general_data_keys[k]])
} }
} }
} }
@@ -577,6 +707,7 @@ func (m *MainWindow) unlockInterface() {
} }
func (m *MainWindow) updateOneServer() { func (m *MainWindow) updateOneServer() {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Updating selected server...</span></markup>"})
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage())) current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
srv_address := m.getIpFromServersList(current_tab) srv_address := m.getIpFromServersList(current_tab)

View File

@@ -18,9 +18,11 @@ import (
// Main window initialization. // Main window initialization.
func (m *MainWindow) Initialize() { func (m *MainWindow) Initialize() {
gtk.Init(nil) gtk.Init(nil)
m.initializeStorages() m.initializeStorages()
ctx.InitializeClipboardWatcher()
m.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL) m.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
m.window.SetTitle("URTrator") m.window.SetTitle("URTrator")
@@ -128,7 +130,7 @@ func (m *MainWindow) Initialize() {
m.vbox.PackStart(profile_and_launch_hbox, false, true, 5) m.vbox.PackStart(profile_and_launch_hbox, false, true, 5)
// Separator // Separator
sep := gtk.NewHSeparator() sep := gtk.NewHBox(false, 0)
profile_and_launch_hbox.PackStart(sep, true, true, 5) profile_and_launch_hbox.PackStart(sep, true, true, 5)
// Profile selection. // Profile selection.
@@ -147,7 +149,7 @@ func (m *MainWindow) Initialize() {
m.launch_button = gtk.NewButtonWithLabel("Launch!") m.launch_button = gtk.NewButtonWithLabel("Launch!")
m.launch_button.SetTooltipText("Launch Urban Terror") m.launch_button.SetTooltipText("Launch Urban Terror")
m.launch_button.Clicked(m.launchGame) m.launch_button.Clicked(m.launchGame)
launch_button_image := gtk.NewImageFromStock(gtk.STOCK_APPLY, 24) launch_button_image := gtk.NewImageFromPixbuf(logo.ScaleSimple(24, 24, gdkpixbuf.INTERP_HYPER))
m.launch_button.SetImage(launch_button_image) m.launch_button.SetImage(launch_button_image)
profile_and_launch_hbox.PackStart(m.launch_button, false, true, 5) profile_and_launch_hbox.PackStart(m.launch_button, false, true, 5)
@@ -163,6 +165,9 @@ func (m *MainWindow) Initialize() {
ctx.Eventer.LaunchEvent("loadFavoriteServers", map[string]string{}) ctx.Eventer.LaunchEvent("loadFavoriteServers", map[string]string{})
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "URTrator is ready."}) ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "URTrator is ready."})
// Set flag that shows to other parts that we're initialized.
m.initialized = true
gtk.Main() gtk.Main()
} }
@@ -173,6 +178,7 @@ func (m *MainWindow) initializeEvents() {
ctx.Eventer.AddEventHandler("loadFavoriteServers", m.loadFavoriteServers) ctx.Eventer.AddEventHandler("loadFavoriteServers", m.loadFavoriteServers)
ctx.Eventer.AddEventHandler("loadProfilesIntoMainWindow", m.loadProfiles) ctx.Eventer.AddEventHandler("loadProfilesIntoMainWindow", m.loadProfiles)
ctx.Eventer.AddEventHandler("serversUpdateCompleted", m.serversUpdateCompleted) ctx.Eventer.AddEventHandler("serversUpdateCompleted", m.serversUpdateCompleted)
ctx.Eventer.AddEventHandler("setQuickConnectDetails", m.setQuickConnectDetails)
ctx.Eventer.AddEventHandler("setToolbarLabelText", m.setToolbarLabelText) ctx.Eventer.AddEventHandler("setToolbarLabelText", m.setToolbarLabelText)
} }
@@ -247,11 +253,6 @@ func (m *MainWindow) initializeSidebar() {
si_scroll.Add(m.server_info) si_scroll.Add(m.server_info)
// Button to view additional server info.
additional_srv_info_button := gtk.NewButtonWithLabel("Additional information")
additional_srv_info_button.Clicked(m.showServerInformation)
si_vbox.PackStart(additional_srv_info_button, false, true, 5)
// Quick connect frame. // Quick connect frame.
quick_connect_frame := gtk.NewFrame("Quick connect") quick_connect_frame := gtk.NewFrame("Quick connect")
sidebar_vbox.PackStart(quick_connect_frame, false, true, 5) sidebar_vbox.PackStart(quick_connect_frame, false, true, 5)
@@ -293,6 +294,9 @@ func (m *MainWindow) initializeSidebar() {
// Initializes internal storages. // Initializes internal storages.
func (m *MainWindow) initializeStorages() { func (m *MainWindow) initializeStorages() {
// Application isn't initialized.
m.initialized = false
m.use_other_servers_tab = false
// Gamemodes. // Gamemodes.
m.gamemodes = make(map[string]string) m.gamemodes = make(map[string]string)
m.gamemodes = map[string]string{ m.gamemodes = map[string]string{
@@ -351,19 +355,37 @@ func (m *MainWindow) initializeStorages() {
// Pixbufs. // Pixbufs.
// Offline server. // Offline server.
m.server_offline_pic = gtk.NewImage().RenderIcon(gtk.STOCK_NO, gtk.ICON_SIZE_SMALL_TOOLBAR, "") srv_offline_bytes, _ := base64.StdEncoding.DecodeString(common.SERVER_OFFLINE)
srv_offline_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
srv_offline_pixbuf.SetSize(24, 24)
srv_offline_pixbuf.Write(srv_offline_bytes)
m.server_offline_pic = srv_offline_pixbuf.GetPixbuf()
// Online server. // Online server.
m.server_online_pic = gtk.NewImage().RenderIcon(gtk.STOCK_OK, gtk.ICON_SIZE_SMALL_TOOLBAR, "") srv_online_bytes, _ := base64.StdEncoding.DecodeString(common.SERVER_ONLINE)
// Passworded server. srv_online_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
m.server_passworded_pic = gtk.NewImage().RenderIcon(gtk.STOCK_CLOSE, gtk.ICON_SIZE_SMALL_TOOLBAR, "") srv_online_pixbuf.SetSize(24, 24)
srv_online_pixbuf.Write(srv_online_bytes)
m.server_online_pic = srv_online_pixbuf.GetPixbuf()
// Private server.
srv_private_bytes, _ := base64.StdEncoding.DecodeString(common.SERVER_PRIVATE)
srv_private_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
srv_private_pixbuf.SetSize(24, 24)
srv_private_pixbuf.Write(srv_private_bytes)
m.server_private_pic = srv_private_pixbuf.GetPixbuf()
// Public server. // Public server.
m.server_public_pic = gtk.NewImage().RenderIcon(gtk.STOCK_OK, gtk.ICON_SIZE_SMALL_TOOLBAR, "") srv_public_bytes, _ := base64.StdEncoding.DecodeString(common.SERVER_PUBLIC)
srv_public_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
srv_public_pixbuf.SetSize(24, 24)
srv_public_pixbuf.Write(srv_public_bytes)
m.server_public_pic = srv_public_pixbuf.GetPixbuf()
fmt.Println(srv_public_pixbuf.GetFormat().GetName())
} }
// Tabs widget initialization, including all child widgets. // Tabs widget initialization, including all child widgets.
func (m *MainWindow) InitializeTabs() { func (m *MainWindow) InitializeTabs() {
// Create tabs widget. // Create tabs widget.
m.tab_widget = gtk.NewNotebook() m.tab_widget = gtk.NewNotebook()
m.tab_widget.Connect("switch-page", m.tabChanged)
tab_allsrv_hbox := gtk.NewHBox(false, 0) tab_allsrv_hbox := gtk.NewHBox(false, 0)
swin1 := gtk.NewScrolledWindow(nil, nil) swin1 := gtk.NewScrolledWindow(nil, nil)
@@ -419,9 +441,15 @@ func (m *MainWindow) InitializeTabs() {
// Sorting. // Sorting.
// By default we are sorting by server name. // By default we are sorting by server name.
// ToDo: remembering it to configuration storage.
m.all_servers_store_sortable.SetSortColumnId(m.column_pos["Servers"]["Name"], gtk.SORT_ASCENDING) m.all_servers_store_sortable.SetSortColumnId(m.column_pos["Servers"]["Name"], gtk.SORT_ASCENDING)
// Sorting functions.
// Race conditions and GC crazyness appears when activated, so for
// now commenting it out.
m.all_servers_store_sortable.SetSortFunc(m.column_pos["Servers"]["Name"], m.sortServersByName)
m.all_servers_store_sortable.SetSortFunc(m.column_pos["Servers"]["Players"], m.sortServersByPlayers)
m.all_servers_store_sortable.SetSortFunc(m.column_pos["Servers"]["Ping"], m.sortServersByPing)
// Selection changed signal, which will update server's short info pane. // Selection changed signal, which will update server's short info pane.
m.all_servers.Connect("cursor-changed", m.showShortServerInformation) m.all_servers.Connect("cursor-changed", m.showShortServerInformation)
@@ -434,8 +462,30 @@ func (m *MainWindow) InitializeTabs() {
m.all_servers_hide_offline.SetTooltipText("Hide offline servers on Servers tab") m.all_servers_hide_offline.SetTooltipText("Hide offline servers on Servers tab")
tab_all_srv_ctl_vbox.PackStart(m.all_servers_hide_offline, false, true, 5) tab_all_srv_ctl_vbox.PackStart(m.all_servers_hide_offline, false, true, 5)
m.all_servers_hide_offline.Clicked(m.hideOfflineAllServers) m.all_servers_hide_offline.Clicked(m.hideOfflineAllServers)
if ctx.Cfg.Cfg["/serverslist/all_servers/hide_offline"] == "1" { // Restore value of hide offline servers checkbox.
// Set to checked for new installations.
all_servers_hide_offline_cb_val, ok := ctx.Cfg.Cfg["/serverslist/all_servers/hide_offline"]
if !ok {
m.all_servers_hide_offline.SetActive(true) m.all_servers_hide_offline.SetActive(true)
} else {
if all_servers_hide_offline_cb_val == "1" {
m.all_servers_hide_offline.SetActive(true)
}
}
// Checkbox for hiding passworded servers.
m.all_servers_hide_private = gtk.NewCheckButtonWithLabel("Hide private servers")
m.all_servers_hide_private.SetTooltipText("Hide servers which requires password to enter")
tab_all_srv_ctl_vbox.PackStart(m.all_servers_hide_private, false, true, 5)
m.all_servers_hide_private.Clicked(m.hidePrivateAllServers)
// Restore checkbox value.
all_servers_hide_private_cb_val, ok := ctx.Cfg.Cfg["/serverslist/all_servers/hide_private"]
if !ok {
m.all_servers_hide_private.SetActive(true)
} else {
if all_servers_hide_private_cb_val == "1" {
m.all_servers_hide_private.SetActive(true)
}
} }
// Final separator. // Final separator.
@@ -473,7 +523,11 @@ func (m *MainWindow) InitializeTabs() {
width_int, _ := strconv.Atoi(width) width_int, _ := strconv.Atoi(width)
col := gtk.NewTreeViewColumnWithAttributes(name, gtk.NewCellRendererText(), "markup", position_int) col := gtk.NewTreeViewColumnWithAttributes(name, gtk.NewCellRendererText(), "markup", position_int)
col.SetSortColumnId(position_int) // For some reason this cause panic on Windows, so disabling
// default sorting here.
if runtime.GOOS != "windows" {
col.SetSortColumnId(position_int)
}
col.SetReorderable(true) col.SetReorderable(true)
col.SetResizable(true) col.SetResizable(true)
// GtkTreeViewColumn.SetFixedWidth() accepts only positive integers. // GtkTreeViewColumn.SetFixedWidth() accepts only positive integers.
@@ -504,8 +558,30 @@ func (m *MainWindow) InitializeTabs() {
m.fav_servers_hide_offline.SetTooltipText("Hide offline servers on Favorites tab") m.fav_servers_hide_offline.SetTooltipText("Hide offline servers on Favorites tab")
tab_fav_srv_ctl_vbox.PackStart(m.fav_servers_hide_offline, false, true, 5) tab_fav_srv_ctl_vbox.PackStart(m.fav_servers_hide_offline, false, true, 5)
m.fav_servers_hide_offline.Clicked(m.hideOfflineFavoriteServers) m.fav_servers_hide_offline.Clicked(m.hideOfflineFavoriteServers)
if ctx.Cfg.Cfg["/serverslist/favorite/hide_offline"] == "1" { // Restore value of hide offline servers checkbox.
// Set to checked for new installations.
favorite_servers_hide_offline_cb_val, ok := ctx.Cfg.Cfg["/serverslist/favorite/hide_offline"]
if !ok {
m.fav_servers_hide_offline.SetActive(true) m.fav_servers_hide_offline.SetActive(true)
} else {
if favorite_servers_hide_offline_cb_val == "1" {
m.fav_servers_hide_offline.SetActive(true)
}
}
// Checkbox for hiding passworded servers.
m.fav_servers_hide_private = gtk.NewCheckButtonWithLabel("Hide private servers")
m.fav_servers_hide_private.SetTooltipText("Hide servers which requires password to enter")
tab_fav_srv_ctl_vbox.PackStart(m.fav_servers_hide_private, false, true, 5)
m.fav_servers_hide_private.Clicked(m.hidePrivateFavoriteServers)
// Restore checkbox value.
fav_servers_hide_private_cb_val, ok := ctx.Cfg.Cfg["/serverslist/favorite/hide_private"]
if !ok {
m.fav_servers_hide_private.SetActive(true)
} else {
if fav_servers_hide_private_cb_val == "1" {
m.fav_servers_hide_private.SetActive(true)
}
} }
// Final separator. // Final separator.
@@ -522,14 +598,22 @@ func (m *MainWindow) InitializeToolbar() {
m.vbox.PackStart(m.toolbar, false, false, 5) m.vbox.PackStart(m.toolbar, false, false, 5)
// Update servers button. // Update servers button.
button_update_all_servers := gtk.NewToolButtonFromStock(gtk.STOCK_REFRESH) button_update_all_servers_icon_bytes, _ := base64.StdEncoding.DecodeString(common.REFRESH_ALL_SERVERS)
button_update_all_servers.SetLabel("Update all servers") button_update_all_servers_icon_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
button_update_all_servers.SetTooltipText("Update all servers in all tabs") button_update_all_servers_icon_pixbuf.SetSize(24, 24)
button_update_all_servers_icon_pixbuf.Write(button_update_all_servers_icon_bytes)
button_update_all_servers_icon := gtk.NewImageFromPixbuf(button_update_all_servers_icon_pixbuf.GetPixbuf())
button_update_all_servers := gtk.NewToolButton(button_update_all_servers_icon, "Update all servers")
button_update_all_servers.SetTooltipText("Update all servers in currently selected tab")
button_update_all_servers.OnClicked(m.UpdateServers) button_update_all_servers.OnClicked(m.UpdateServers)
m.toolbar.Insert(button_update_all_servers, 0) m.toolbar.Insert(button_update_all_servers, 0)
button_update_one_server := gtk.NewToolButtonFromStock(gtk.STOCK_REDO) button_update_one_server_icon_bytes, _ := base64.StdEncoding.DecodeString(common.REFRESH_ONE_SERVER)
button_update_one_server.SetLabel("Update selected server") button_update_one_server_icon_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
button_update_one_server_icon_pixbuf.SetSize(24, 24)
button_update_one_server_icon_pixbuf.Write(button_update_one_server_icon_bytes)
button_update_one_server_icon := gtk.NewImageFromPixbuf(button_update_one_server_icon_pixbuf.GetPixbuf())
button_update_one_server := gtk.NewToolButton(button_update_one_server_icon, "Update selected server")
button_update_one_server.SetTooltipText("Update only selected server") button_update_one_server.SetTooltipText("Update only selected server")
button_update_one_server.OnClicked(m.updateOneServer) button_update_one_server.OnClicked(m.updateOneServer)
m.toolbar.Insert(button_update_one_server, 1) m.toolbar.Insert(button_update_one_server, 1)
@@ -539,34 +623,57 @@ func (m *MainWindow) InitializeToolbar() {
m.toolbar.Insert(separator, 2) m.toolbar.Insert(separator, 2)
// Add server to favorites button. // Add server to favorites button.
fav_button := gtk.NewToolButtonFromStock(gtk.STOCK_ADD) fav_button_icon_bytes, _ := base64.StdEncoding.DecodeString(common.ADD_TO_FAVORITES)
fav_button.SetLabel("Add to favorites") fav_button_icon_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
fav_button_icon_pixbuf.SetSize(24, 24)
fav_button_icon_pixbuf.Write(fav_button_icon_bytes)
fav_button_icon := gtk.NewImageFromPixbuf(fav_button_icon_pixbuf.GetPixbuf())
fav_button := gtk.NewToolButton(fav_button_icon, "Add to favorites")
fav_button.SetTooltipText("Add selected server to favorites") fav_button.SetTooltipText("Add selected server to favorites")
fav_button.OnClicked(m.addToFavorites) fav_button.OnClicked(m.addToFavorites)
m.toolbar.Insert(fav_button, 3) m.toolbar.Insert(fav_button, 3)
fav_edit_button := gtk.NewToolButtonFromStock(gtk.STOCK_EDIT) fav_edit_button_icon_bytes, _ := base64.StdEncoding.DecodeString(common.EDIT_FAVORITE)
fav_edit_button.SetLabel("Edit favorite") fav_edit_button_icon_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
fav_edit_button_icon_pixbuf.SetSize(24, 24)
fav_edit_button_icon_pixbuf.Write(fav_edit_button_icon_bytes)
fav_edit_button_icon := gtk.NewImageFromPixbuf(fav_edit_button_icon_pixbuf.GetPixbuf())
fav_edit_button := gtk.NewToolButton(fav_edit_button_icon, "Edit favorite")
fav_edit_button.SetTooltipText("Edit selected favorite server") fav_edit_button.SetTooltipText("Edit selected favorite server")
fav_edit_button.OnClicked(m.editFavorite) fav_edit_button.OnClicked(m.editFavorite)
m.toolbar.Insert(fav_edit_button, 4) m.toolbar.Insert(fav_edit_button, 4)
// Remove server from favorites button. // Remove server from favorites button.
fav_delete_button := gtk.NewToolButtonFromStock(gtk.STOCK_REMOVE) fav_delete_button_icon_bytes, _ := base64.StdEncoding.DecodeString(common.REMOVE_FAVORITE)
fav_delete_button.SetLabel("Remove from favorites") fav_delete_button_icon_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
fav_delete_button_icon_pixbuf.SetSize(24, 24)
fav_delete_button_icon_pixbuf.Write(fav_delete_button_icon_bytes)
fav_delete_button_icon := gtk.NewImageFromPixbuf(fav_delete_button_icon_pixbuf.GetPixbuf())
fav_delete_button := gtk.NewToolButton(fav_delete_button_icon, "Remove from favorites")
fav_delete_button.SetTooltipText("Remove selected server from favorites") fav_delete_button.SetTooltipText("Remove selected server from favorites")
fav_delete_button.OnClicked(m.deleteFromFavorites) fav_delete_button.OnClicked(m.deleteFromFavorites)
m.toolbar.Insert(fav_delete_button, 5) m.toolbar.Insert(fav_delete_button, 5)
// Copy server address button.
copy_srv_addr_button_icon_bytes, _ := base64.StdEncoding.DecodeString(common.COPY_CREDENTIALS)
copy_srv_addr_button_icon_pixbuf, _ := gdkpixbuf.NewLoaderWithType("png")
copy_srv_addr_button_icon_pixbuf.SetSize(24, 24)
copy_srv_addr_button_icon_pixbuf.Write(copy_srv_addr_button_icon_bytes)
copy_srv_addr_button_icon := gtk.NewImageFromPixbuf(copy_srv_addr_button_icon_pixbuf.GetPixbuf())
copy_srv_addr_button := gtk.NewToolButton(copy_srv_addr_button_icon, "Copy server's creds")
copy_srv_addr_button.SetTooltipText("Copy server's credentials to clipboard for sharing")
copy_srv_addr_button.OnClicked(m.copyServerCredentialsToClipboard)
m.toolbar.Insert(copy_srv_addr_button, 6)
// Separator for toolbar's label and buttons. // Separator for toolbar's label and buttons.
toolbar_separator_toolitem := gtk.NewToolItem() toolbar_separator_toolitem := gtk.NewToolItem()
toolbar_separator_toolitem.SetExpand(true) toolbar_separator_toolitem.SetExpand(true)
m.toolbar.Insert(toolbar_separator_toolitem, 6) m.toolbar.Insert(toolbar_separator_toolitem, 7)
// Toolbar's label. // Toolbar's label.
m.toolbar_label = gtk.NewLabel("URTrator is ready") m.toolbar_label = gtk.NewLabel("URTrator is ready")
toolbar_label_toolitem := gtk.NewToolItem() toolbar_label_toolitem := gtk.NewToolItem()
toolbar_label_toolitem.Add(m.toolbar_label) toolbar_label_toolitem.Add(m.toolbar_label)
m.toolbar.Insert(toolbar_label_toolitem, 7) m.toolbar.Insert(toolbar_label_toolitem, 8)
} }
// Tray icon initialization. // Tray icon initialization.

View File

@@ -4,6 +4,7 @@ import (
// stdlib // stdlib
"errors" "errors"
"fmt" "fmt"
"runtime"
"strings" "strings"
// Local // Local
@@ -51,25 +52,46 @@ func (m *MainWindow) launchAsUsual() error {
model.GetValue(iter, m.column_pos["Favorites"]["IP"], srv_address_gval) model.GetValue(iter, m.column_pos["Favorites"]["IP"], srv_address_gval)
} }
srv_address = srv_address_gval.GetString() srv_address = srv_address_gval.GetString()
if len(srv_address) == 0 {
// Temporary disable all these modals on Linux.
// See https://github.com/mattn/go-gtk/issues/289.
if runtime.GOOS != "linux" {
mbox_string := "No server selected.\n\nPlease, select a server to continue connecting."
messagebox := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
messagebox.Response(func() {
messagebox.Destroy()
})
messagebox.Run()
} else {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Select a server we will connect to!</span></markup>"})
}
return errors.New("No server selected.")
}
server_profile := ctx.Cache.Servers[srv_address].Server server_profile := ctx.Cache.Servers[srv_address].Server
// Check for proper server name. If length == 0: server is offline, // Check for proper server name. If length == 0: server is offline,
// we should show notification to user. // we should show notification to user.
if len(server_profile.Name) == 0 { if len(server_profile.Name) == 0 {
var will_continue bool = false 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." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Connect("response", func(resp *glib.CallbackContext) { if runtime.GOOS != "linux" {
if resp.Args(0) == 4294967287 { 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."
will_continue = false messagebox := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, mbox_string)
} else { messagebox.Connect("response", func(resp *glib.CallbackContext) {
will_continue = true if resp.Args(0) == 4294967287 {
} will_continue = false
}) } else {
m.Response(func() { will_continue = true
m.Destroy() }
}) messagebox.Destroy()
m.Run() })
messagebox.Run()
} else {
// We're okay to connect to empty server, temporary.
will_continue = true
}
if !will_continue { if !will_continue {
return errors.New("User declined to connect to offline server") return errors.New("User declined to connect to offline server")
} }
@@ -83,19 +105,41 @@ func (m *MainWindow) launchAsUsual() error {
// This check only relevant to "Servers" tab, favorite servers // This check only relevant to "Servers" tab, favorite servers
// have profiles defined (see next). // have profiles defined (see next).
if len(profile_name) == 0 { if len(profile_name) == 0 {
mbox_string := "Invalid game profile selected.\n\nPlease, select profile and retry." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Response(func() { if runtime.GOOS != "linux" {
m.Destroy() mbox_string := "Invalid game profile selected.\n\nPlease, select profile and retry."
}) messagebox := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Run() messagebox.Response(func() {
messagebox.Destroy()
})
messagebox.Run()
} else {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Invalid game profile selected.</span></markup>"})
}
return errors.New("User didn't select valid profile.") return errors.New("User didn't select valid profile.")
} }
user_profile = ctx.Cache.Profiles[profile_name].Profile user_profile = ctx.Cache.Profiles[profile_name].Profile
} else if strings.Contains(current_tab, "Favorites") { } else if strings.Contains(current_tab, "Favorites") {
// For favorite servers profile specified in favorite server // For favorite servers profile specified in favorite server
// information have higher priority, so we just override it :) // information have higher priority, so we just override it :)
user_profile = ctx.Cache.Profiles[server_profile.ProfileToUse].Profile user_profile_cached, ok := ctx.Cache.Profiles[server_profile.ProfileToUse]
if !ok {
// Temporary disable all these modals on Linux.
// See https://github.com/mattn/go-gtk/issues/289.
if runtime.GOOS != "linux" {
mbox_string := "Invalid game profile specified for favorite server.\n\nPlease, edit your favorite server, select valid profile and retry."
messagebox := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
messagebox.Response(func() {
messagebox.Destroy()
})
messagebox.Run()
} else {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Invalid game profile specified in favorite entry.</span></markup>"})
}
return errors.New("User didn't select valid profile.")
}
user_profile = user_profile_cached.Profile
} }
m.launchActually(server_profile, user_profile, "", "") m.launchActually(server_profile, user_profile, "", "")
@@ -144,19 +188,25 @@ func (m *MainWindow) launchWithQuickConnect() error {
func (m *MainWindow) launchActually(server_profile *datamodels.Server, user_profile *datamodels.Profile, password string, nickname_to_use string) error { func (m *MainWindow) launchActually(server_profile *datamodels.Server, user_profile *datamodels.Profile, password string, nickname_to_use string) error {
if server_profile.Name == "" { if server_profile.Name == "" {
var will_continue bool = false 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." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Connect("response", func(resp *glib.CallbackContext) { if runtime.GOOS != "linux" {
if resp.Args(0) == 4294967287 { 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."
will_continue = false messagebox := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, mbox_string)
} else { messagebox.Connect("response", func(resp *glib.CallbackContext) {
will_continue = true if resp.Args(0) == 4294967287 {
} will_continue = false
}) } else {
m.Response(func() { will_continue = true
m.Destroy() }
}) messagebox.Destroy()
m.Run() })
messagebox.Run()
} else {
// We're ok here, temporary.
will_continue = true
}
if !will_continue { if !will_continue {
return errors.New("User declined to connect to offline server") return errors.New("User declined to connect to offline server")
} }
@@ -164,12 +214,18 @@ func (m *MainWindow) launchActually(server_profile *datamodels.Server, user_prof
// Check if server is applicable for selected profile. // Check if server is applicable for selected profile.
if server_profile.Version != user_profile.Version { if server_profile.Version != user_profile.Version {
mbox_string := "Invalid game profile selected.\n\nSelected profile have different game version than server.\nPlease, select valid profile and retry." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Response(func() { if runtime.GOOS != "linux" {
m.Destroy() mbox_string := "Invalid game profile selected.\n\nSelected profile have different game version than server.\nPlease, select valid profile and retry."
}) messagebox := gtk.NewMessageDialog(m.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Run() messagebox.Response(func() {
messagebox.Destroy()
})
messagebox.Run()
} else {
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Invalid game profile selected.</span></markup>"})
}
return errors.New("User didn't select valid profile, mismatch with server's version.") return errors.New("User didn't select valid profile, mismatch with server's version.")
} }
@@ -183,6 +239,9 @@ func (m *MainWindow) launchActually(server_profile *datamodels.Server, user_prof
srv_name_for_label := server_profile.Name srv_name_for_label := server_profile.Name
if strings.Contains(server_profile.Name, "markup") { if strings.Contains(server_profile.Name, "markup") {
srv_name_for_label = string([]byte(server_profile.Name)[8:len(server_profile.Name)-9]) srv_name_for_label = string([]byte(server_profile.Name)[8:len(server_profile.Name)-9])
} else {
srv_name := ctx.Colorizer.Fix(server_profile.Name)
srv_name_for_label = string([]byte(srv_name)[8:len(srv_name)-9])
} }
// Show great coloured label. // Show great coloured label.
ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Urban Terror is launched with profile </span><span foreground=\"blue\">" + user_profile.Name + "</span> <span foreground=\"red\" font_weight=\"bold\">and connected to </span><span foreground=\"orange\" font_weight=\"bold\">" + srv_name_for_label + "</span></markup>"}) ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "<markup><span foreground=\"red\" font_weight=\"bold\">Urban Terror is launched with profile </span><span foreground=\"blue\">" + user_profile.Name + "</span> <span foreground=\"red\" font_weight=\"bold\">and connected to </span><span foreground=\"orange\" font_weight=\"bold\">" + srv_name_for_label + "</span></markup>"})

View File

@@ -0,0 +1,96 @@
package ui
import (
// stdlib
"strconv"
"strings"
// other
"github.com/mattn/go-gtk/glib"
"github.com/mattn/go-gtk/gtk"
)
func (m *MainWindow) sortServersByName(model *gtk.TreeModel, a *gtk.TreeIter, b *gtk.TreeIter) int {
var name1_raw glib.GValue
var name2_raw glib.GValue
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
if current_tab == "Servers" {
model.GetValue(a, m.column_pos["Servers"]["Name"], &name1_raw)
model.GetValue(b, m.column_pos["Servers"]["Name"], &name2_raw)
} else if current_tab == "Favorites" {
model.GetValue(a, m.column_pos["Favorites"]["Name"], &name1_raw)
model.GetValue(b, m.column_pos["Favorites"]["Name"], &name2_raw)
} else {
return 0
}
name1 := strings.ToLower(ctx.Colorizer.ClearFromMarkup(name1_raw.GetString()))
name2 := strings.ToLower(ctx.Colorizer.ClearFromMarkup(name2_raw.GetString()))
if name1 < name2 {
return -1
} else {
return 1
}
return 0
}
func (m *MainWindow) sortServersByPlayers(model *gtk.TreeModel, a *gtk.TreeIter, b *gtk.TreeIter) int {
var players1_raw glib.GValue
var players2_raw glib.GValue
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
if current_tab == "Servers" {
model.GetValue(a, m.column_pos["Servers"]["Players"], &players1_raw)
model.GetValue(b, m.column_pos["Servers"]["Players"], &players2_raw)
} else if current_tab == "Favorites" {
model.GetValue(a, m.column_pos["Favorites"]["Players"], &players1_raw)
model.GetValue(b, m.column_pos["Favorites"]["Players"], &players2_raw)
} else {
return 0
}
players1_online := strings.Split(players1_raw.GetString(), "/")[0]
players2_online := strings.Split(players2_raw.GetString(), "/")[0]
if len(players1_online) > 0 && len(players2_online) > 0 {
players1, _ := strconv.Atoi(players1_online)
players2, _ := strconv.Atoi(players2_online)
if players1 > players2 {
return -1
} else {
return 1
}
}
return -1
}
func (m *MainWindow) sortServersByPing(model *gtk.TreeModel, a *gtk.TreeIter, b *gtk.TreeIter) int {
var ping1_raw glib.GValue
var ping2_raw glib.GValue
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
if current_tab == "Servers" {
model.GetValue(a, m.column_pos["Servers"]["Ping"], &ping1_raw)
model.GetValue(b, m.column_pos["Servers"]["Ping"], &ping2_raw)
} else if current_tab == "Favorites" {
model.GetValue(a, m.column_pos["Favorites"]["Ping"], &ping1_raw)
model.GetValue(b, m.column_pos["Favorites"]["Ping"], &ping2_raw)
} else {
return 0
}
ping1, _ := strconv.Atoi(ping1_raw.GetString())
ping2, _ := strconv.Atoi(ping2_raw.GetString())
if ping1 < ping2 {
return 1
} else {
return -1
}
return -1
}

View File

@@ -12,6 +12,7 @@ package ui
import ( import (
// stdlib // stdlib
"fmt" "fmt"
"runtime"
// Local // Local
"github.com/pztrn/urtrator/datamodels" "github.com/pztrn/urtrator/datamodels"
@@ -66,12 +67,16 @@ func (o *OptionsDialog) closeOptionsDialogWithSaving() {
o.saveGeneral() o.saveGeneral()
mbox_string := "Some options require application restart to be applied." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(o.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Response(func() { if runtime.GOOS != "linux" {
m.Destroy() mbox_string := "Some options require application restart to be applied."
}) m := gtk.NewMessageDialog(o.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, mbox_string)
m.Run() m.Response(func() {
m.Destroy()
})
m.Run()
}
o.window.Destroy() o.window.Destroy()
} }
@@ -141,15 +146,33 @@ func (o *OptionsDialog) initializeAppearanceTab() {
func (o *OptionsDialog) initializeGeneralTab() { func (o *OptionsDialog) initializeGeneralTab() {
general_vbox := gtk.NewVBox(false, 0) general_vbox := gtk.NewVBox(false, 0)
general_table := gtk.NewTable(2, 2, false)
// Tray icon checkbox. // Tray icon checkbox.
o.show_tray_icon = gtk.NewCheckButtonWithLabel("Show tray icon?") show_tray_icon_label := gtk.NewLabel("Show icon in tray")
show_tray_icon_label.SetAlignment(0, 0)
general_table.Attach(show_tray_icon_label, 0, 1, 0, 1, gtk.FILL, gtk.SHRINK, 5, 5)
o.show_tray_icon = gtk.NewCheckButtonWithLabel("")
o.show_tray_icon.SetTooltipText("Show icon in tray") o.show_tray_icon.SetTooltipText("Show icon in tray")
general_vbox.PackStart(o.show_tray_icon, false, true, 5) general_table.Attach(o.show_tray_icon, 1, 2, 0, 1, gtk.FILL, gtk.FILL, 5, 5)
// Autoupdate checkbox. // Autoupdate checkbox.
o.autoupdate = gtk.NewCheckButtonWithLabel("Automatically update URTrator?") autoupdate_tooltip := "Should URTrator check for updates and update itself? Not working now."
o.autoupdate.SetTooltipText("Should URTrator check for updates and update itself? Not working now.") autoupdate_label := gtk.NewLabel("Automatically update URTrator?")
general_vbox.PackStart(o.autoupdate, false, true, 5) autoupdate_label.SetTooltipText(autoupdate_tooltip)
autoupdate_label.SetAlignment(0, 0)
general_table.Attach(autoupdate_label, 0, 1, 1, 2, gtk.FILL, gtk.SHRINK, 5, 5)
o.autoupdate = gtk.NewCheckButtonWithLabel("")
o.autoupdate.SetTooltipText(autoupdate_tooltip)
general_table.Attach(o.autoupdate, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 5, 5)
// Vertical separator.
sep := gtk.NewVBox(false, 0)
general_vbox.PackStart(general_table, false, true, 0)
general_vbox.PackStart(sep, false, true, 0)
o.tab_widget.AppendPage(general_vbox, gtk.NewLabel("General")) o.tab_widget.AppendPage(general_vbox, gtk.NewLabel("General"))
} }
@@ -171,7 +194,7 @@ func (o *OptionsDialog) initializeTabs() {
// Buttons for saving and discarding changes. // Buttons for saving and discarding changes.
buttons_hbox := gtk.NewHBox(false, 0) buttons_hbox := gtk.NewHBox(false, 0)
sep := gtk.NewHSeparator() sep := gtk.NewHBox(false, 0)
cancel_button := gtk.NewButtonWithLabel("Cancel") cancel_button := gtk.NewButtonWithLabel("Cancel")
cancel_button.Clicked(o.closeOptionsDialogByCancel) cancel_button.Clicked(o.closeOptionsDialogByCancel)
@@ -190,7 +213,7 @@ func (o *OptionsDialog) initializeTabs() {
} }
func (o *OptionsDialog) initializeUrtTab() { func (o *OptionsDialog) initializeUrtTab() {
urt_hbox := gtk.NewHBox(false, 0) urt_hbox := gtk.NewHBox(false, 5)
// Profiles list. // Profiles list.
o.profiles_list = gtk.NewTreeView() o.profiles_list = gtk.NewTreeView()
@@ -200,17 +223,13 @@ func (o *OptionsDialog) initializeUrtTab() {
o.profiles_list.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Profile name", gtk.NewCellRendererText(), "text", 0)) o.profiles_list.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Profile name", gtk.NewCellRendererText(), "text", 0))
o.profiles_list.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Urban Terror version", gtk.NewCellRendererText(), "text", 1)) o.profiles_list.AppendColumn(gtk.NewTreeViewColumnWithAttributes("Urban Terror version", gtk.NewCellRendererText(), "text", 1))
//crt := gtk.NewCellRendererToggle()
//second_x_column := gtk.NewTreeViewColumnWithAttributes("Second X session", crt, "bool", 2)
//o.profiles_list.AppendColumn(second_x_column)
// Profiles list buttons. // Profiles list buttons.
urt_profiles_buttons_vbox := gtk.NewVBox(false, 0) urt_profiles_buttons_vbox := gtk.NewVBox(false, 0)
button_add := gtk.NewButtonWithLabel("Add") button_add := gtk.NewButtonWithLabel("Add")
button_add.SetTooltipText("Add new profile") button_add.SetTooltipText("Add new profile")
button_add.Clicked(o.addProfile) button_add.Clicked(o.addProfile)
urt_profiles_buttons_vbox.PackStart(button_add, false, true, 5) urt_profiles_buttons_vbox.PackStart(button_add, false, true, 0)
button_edit := gtk.NewButtonWithLabel("Edit") button_edit := gtk.NewButtonWithLabel("Edit")
button_edit.SetTooltipText("Edit selected profile. Do nothing if no profile was selected.") button_edit.SetTooltipText("Edit selected profile. Do nothing if no profile was selected.")
@@ -218,13 +237,13 @@ func (o *OptionsDialog) initializeUrtTab() {
urt_profiles_buttons_vbox.PackStart(button_edit, false, true, 5) urt_profiles_buttons_vbox.PackStart(button_edit, false, true, 5)
// Spacer for profiles list buttons. // Spacer for profiles list buttons.
sep := gtk.NewVSeparator() sep := gtk.NewVBox(false, 0)
urt_profiles_buttons_vbox.PackStart(sep, true, true, 5) urt_profiles_buttons_vbox.PackStart(sep, true, true, 5)
button_delete := gtk.NewButtonWithLabel("Delete") button_delete := gtk.NewButtonWithLabel("Delete")
button_delete.SetTooltipText("Delete selected profile. Do nothing if no profile was selected.") button_delete.SetTooltipText("Delete selected profile. Do nothing if no profile was selected.")
button_delete.Clicked(o.deleteProfile) button_delete.Clicked(o.deleteProfile)
urt_profiles_buttons_vbox.PackStart(button_delete, false, true, 5) urt_profiles_buttons_vbox.PackStart(button_delete, false, true, 0)
urt_hbox.Add(urt_profiles_buttons_vbox) urt_hbox.Add(urt_profiles_buttons_vbox)

View File

@@ -27,8 +27,8 @@ import (
type OptionsProfile struct { type OptionsProfile struct {
// Window. // Window.
window *gtk.Window window *gtk.Window
// Main Vertical Box. // Main table.
vbox *gtk.VBox table *gtk.Table
// Profile name. // Profile name.
profile_name *gtk.Entry profile_name *gtk.Entry
// Binary path. // Binary path.
@@ -71,12 +71,18 @@ func (op *OptionsProfile) browseForBinaryHelper() {
if runtime.GOARCH == "amd64" { if runtime.GOARCH == "amd64" {
if len(filename) > 0 && strings.Split(filename, ".")[1] != "x86_64" && strings.Split(filename, ".")[0] != "Quake3-UrT" { if len(filename) > 0 && strings.Split(filename, ".")[1] != "x86_64" && strings.Split(filename, ".")[0] != "Quake3-UrT" {
fmt.Println("Invalid binary selected!") fmt.Println("Invalid binary selected!")
mbox_string := "Invalid binary selected!\nAccording to your OS, it should be Quake3-UrT.x86_64." // Temporary disable all these modals on Linux.
m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) // See https://github.com/mattn/go-gtk/issues/289.
m.Response(func() { if runtime.GOOS != "linux" {
m.Destroy() mbox_string := "Invalid binary selected!\nAccording to your OS, it should be Quake3-UrT.x86_64."
}) m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Run() m.Response(func() {
m.Destroy()
})
m.Run()
} else {
//
}
op.binary_path.SetText("") op.binary_path.SetText("")
} }
} }
@@ -87,21 +93,33 @@ func (op *OptionsProfile) browseForBinaryHelper() {
filename = strings.Split(filename, "Quake3-UrT.app")[1] filename = strings.Split(filename, "Quake3-UrT.app")[1]
if len(filename) > 0 && !strings.Contains(strings.Split(filename, ".")[1], "x86_64") && !strings.Contains(strings.Split(filename, ".")[0], "Quake3-UrT") { if len(filename) > 0 && !strings.Contains(strings.Split(filename, ".")[1], "x86_64") && !strings.Contains(strings.Split(filename, ".")[0], "Quake3-UrT") {
fmt.Println("Invalid binary selected!") fmt.Println("Invalid binary selected!")
mbox_string := "Invalid binary selected!\nAccording to your OS, it should be Quake3-UrT.app/Contents/MacOS/Quake3-UrT.x86_64." // Temporary disable all these modals on Linux.
// See https://github.com/mattn/go-gtk/issues/289.
if runtime.GOOS != "linux" {
mbox_string := "Invalid binary selected!\nAccording to your OS, it should be Quake3-UrT.app/Contents/MacOS/Quake3-UrT.x86_64."
m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Response(func() {
m.Destroy()
})
m.Run()
} else {
//
}
op.binary_path.SetText("")
}
} else {
// Temporary disable all these modals on Linux.
// See https://github.com/mattn/go-gtk/issues/289.
if runtime.GOOS != "linux" {
mbox_string := "Invalid binary selected!\nAccording to your OS, it should be Quake3-UrT.app/Contents/MacOS/Quake3-UrT.x86_64.\n\nNote, that currently URTrator supports only official binary."
m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string) m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Response(func() { m.Response(func() {
m.Destroy() m.Destroy()
}) })
m.Run() m.Run()
op.binary_path.SetText("") } else {
//
} }
} else {
mbox_string := "Invalid binary selected!\nAccording to your OS, it should be Quake3-UrT.app/Contents/MacOS/Quake3-UrT.x86_64.\n\nNote, that currently URTrator supports only official binary."
m := gtk.NewMessageDialog(op.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, mbox_string)
m.Response(func() {
m.Destroy()
})
m.Run()
} }
} }
} }
@@ -130,90 +148,85 @@ func (op *OptionsProfile) Initialize(update bool) {
op.window.SetPosition(gtk.WIN_POS_CENTER) op.window.SetPosition(gtk.WIN_POS_CENTER)
op.window.SetIcon(logo) op.window.SetIcon(logo)
op.vbox = gtk.NewVBox(false, 0) op.table = gtk.NewTable(6, 2, false)
op.table.SetRowSpacings(2)
// Profile name. // Profile name.
profile_name_tooltip := "This how you will see profile on profiles lists." profile_name_tooltip := "This how you will see profile on profiles lists."
pn_hbox := gtk.NewHBox(false, 0)
pn_label := gtk.NewLabel("Profile name:") pn_label := gtk.NewLabel("Profile name:")
pn_label.SetTooltipText(profile_name_tooltip) pn_label.SetTooltipText(profile_name_tooltip)
profile_name_sep := gtk.NewHSeparator() pn_label.SetAlignment(0, 0)
profile_name_sep.SetTooltipText(profile_name_tooltip) op.table.Attach(pn_label, 0, 1, 0, 1, gtk.FILL, gtk.SHRINK, 5, 5)
op.profile_name = gtk.NewEntry() op.profile_name = gtk.NewEntry()
op.profile_name.SetTooltipText(profile_name_tooltip) op.profile_name.SetTooltipText(profile_name_tooltip)
pn_hbox.PackStart(pn_label, false, true, 5) op.table.Attach(op.profile_name, 1, 2, 0, 1, gtk.FILL, gtk.FILL, 5, 5)
pn_hbox.PackStart(profile_name_sep, true, true, 5)
pn_hbox.PackStart(op.profile_name, true, true, 5)
op.vbox.PackStart(pn_hbox, false, true, 5)
// Urban Terror version. // Urban Terror version.
urt_version_tooltip := "Urban Terror version for which this profile applies." urt_version_tooltip := "Urban Terror version for which this profile applies."
urt_version_hbox := gtk.NewHBox(false, 0)
urt_version_label := gtk.NewLabel("Urban Terror version:") urt_version_label := gtk.NewLabel("Urban Terror version:")
urt_version_label.SetTooltipText(urt_version_tooltip) urt_version_label.SetTooltipText(urt_version_tooltip)
urt_version_sep := gtk.NewHSeparator() urt_version_label.SetAlignment(0, 0)
urt_version_sep.SetTooltipText(urt_version_tooltip) op.table.Attach(urt_version_label, 0, 1, 1, 2, gtk.FILL, gtk.SHRINK, 5, 5)
op.urt_version_combo = gtk.NewComboBoxText() op.urt_version_combo = gtk.NewComboBoxText()
op.urt_version_combo.SetTooltipText(urt_version_tooltip) op.urt_version_combo.SetTooltipText(urt_version_tooltip)
op.urt_version_combo.AppendText("4.2.023") op.urt_version_combo.AppendText("4.2.023")
op.urt_version_combo.AppendText("4.3.0") op.urt_version_combo.AppendText("4.3.0")
op.urt_version_combo.AppendText("4.3.1") op.urt_version_combo.AppendText("4.3.1")
op.urt_version_combo.SetActive(1) op.urt_version_combo.SetActive(2)
urt_version_hbox.PackStart(urt_version_label, false, true, 5) op.table.Attach(op.urt_version_combo, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 5, 5)
urt_version_hbox.PackStart(urt_version_sep, true, true, 5)
urt_version_hbox.PackStart(op.urt_version_combo, true, true, 5)
op.vbox.PackStart(urt_version_hbox, false, true, 5)
// Urban Terror binary path. // Urban Terror binary path.
select_binary_tooltip := "Urban Terror binary. Some checks will be executed, so make sure you have selected right binary:\n\nQuake3-UrT.i386 for linux-x86\nQuake3-UrT.x86_64 for linux-amd64\nQuake3-UrT.app for macOS" select_binary_tooltip := "Urban Terror binary. Some checks will be executed, so make sure you have selected right binary:\n\nQuake3-UrT.i386 for linux-x86\nQuake3-UrT.x86_64 for linux-amd64\nQuake3-UrT.app for macOS"
binpath_hbox := gtk.NewHBox(false, 0) binpath_hbox := gtk.NewHBox(false, 0)
binpath_label := gtk.NewLabel("Urban Terror binary:") binpath_label := gtk.NewLabel("Urban Terror binary:")
binpath_label.SetTooltipText(select_binary_tooltip) binpath_label.SetTooltipText(select_binary_tooltip)
binpath_sep := gtk.NewHSeparator() binpath_label.SetAlignment(0, 0)
binpath_sep.SetTooltipText(select_binary_tooltip) op.table.Attach(binpath_label, 0, 1, 2, 3, gtk.FILL, gtk.SHRINK, 5, 5)
op.binary_path = gtk.NewEntry() op.binary_path = gtk.NewEntry()
op.binary_path.SetTooltipText(select_binary_tooltip) op.binary_path.SetTooltipText(select_binary_tooltip)
button_select_binary := gtk.NewButtonWithLabel("Browse") button_select_binary := gtk.NewButtonWithLabel("Browse")
button_select_binary.SetTooltipText(select_binary_tooltip) button_select_binary.SetTooltipText(select_binary_tooltip)
button_select_binary.Clicked(op.browseForBinary) button_select_binary.Clicked(op.browseForBinary)
binpath_hbox.PackStart(binpath_label, false, true, 5)
binpath_hbox.PackStart(binpath_sep, true, true, 5)
binpath_hbox.PackStart(op.binary_path, true, true, 5) binpath_hbox.PackStart(op.binary_path, true, true, 5)
binpath_hbox.PackStart(button_select_binary, false, true, 5) binpath_hbox.PackStart(button_select_binary, false, true, 5)
op.vbox.PackStart(binpath_hbox, false, true, 5) op.table.Attach(binpath_hbox, 1, 2, 2, 3, gtk.FILL, gtk.FILL, 0, 0)
// Should we use additional X session? // Should we use additional X session?
another_x_tooltip := "If this is checked, Urban Terror will be launched in another X session.\n\nThis could help if you're experiencing visual lag, glitches and FPS drops under compositing WMs, like Mutter and KWin." another_x_tooltip := "If this is checked, Urban Terror will be launched in another X session.\n\nThis could help if you're experiencing visual lag, glitches and FPS drops under compositing WMs, like Mutter and KWin."
op.another_x_session = gtk.NewCheckButtonWithLabel("Start Urban Terror in another X session?") another_x_label := gtk.NewLabel("Start Urban Terror in another X session?")
another_x_label.SetTooltipText(another_x_tooltip)
another_x_label.SetAlignment(0, 0)
op.table.Attach(another_x_label, 0, 1, 3, 4, gtk.FILL, gtk.SHRINK, 5, 5)
op.another_x_session = gtk.NewCheckButtonWithLabel("")
op.another_x_session.SetTooltipText(another_x_tooltip) op.another_x_session.SetTooltipText(another_x_tooltip)
op.vbox.PackStart(op.another_x_session, false, true, 5) // macOS and Windows can't do that :).
// macOS can't do that :).
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
op.another_x_session.SetSensitive(false) op.another_x_session.SetSensitive(false)
} }
op.table.Attach(op.another_x_session, 1, 2, 3, 4, gtk.FILL, gtk.FILL, 5, 5)
// Additional game parameters. // Additional game parameters.
params_tooltip := "Additional parameters that will be passed to Urban Terror executable." params_tooltip := "Additional parameters that will be passed to Urban Terror executable."
params_hbox := gtk.NewHBox(false, 0)
params_label := gtk.NewLabel("Additional parameters:") params_label := gtk.NewLabel("Additional parameters:")
params_label.SetTooltipText(params_tooltip) params_label.SetTooltipText(params_tooltip)
params_sep := gtk.NewHSeparator() params_label.SetAlignment(0, 0)
params_sep.SetTooltipText(params_tooltip) op.table.Attach(params_label, 0, 1, 4, 5, gtk.FILL, gtk.SHRINK, 5, 5)
op.additional_parameters = gtk.NewEntry() op.additional_parameters = gtk.NewEntry()
op.additional_parameters.SetTooltipText(params_tooltip) op.additional_parameters.SetTooltipText(params_tooltip)
params_hbox.PackStart(params_label, false, true, 5) op.table.Attach(op.additional_parameters, 1, 2, 4, 5, gtk.FILL, gtk.FILL, 5, 5)
params_hbox.PackStart(params_sep, true, true, 5)
params_hbox.PackStart(op.additional_parameters, true, true, 5)
op.vbox.PackStart(params_hbox, false, true, 5)
// Vertical separator. // Invisible thing.
vert_sep := gtk.NewVSeparator() inv_label := gtk.NewLabel("")
op.vbox.PackStart(vert_sep, true, true, 5) op.table.Attach(inv_label, 1, 2, 5, 6, gtk.EXPAND, gtk.FILL, 5, 5)
// The buttons. // The buttons.
buttons_box := gtk.NewHBox(false, 0) buttons_box := gtk.NewHBox(false, 0)
buttons_sep := gtk.NewHSeparator() buttons_sep := gtk.NewHBox(false, 0)
cancel_button := gtk.NewButtonWithLabel("Cancel") cancel_button := gtk.NewButtonWithLabel("Cancel")
cancel_button.SetTooltipText("Close without saving") cancel_button.SetTooltipText("Close without saving")
@@ -233,9 +246,14 @@ func (op *OptionsProfile) Initialize(update bool) {
add_button.Clicked(op.saveProfile) add_button.Clicked(op.saveProfile)
buttons_box.PackStart(add_button, false, true, 5) buttons_box.PackStart(add_button, false, true, 5)
op.vbox.PackStart(buttons_box, false, true, 5) vert_sep_box := gtk.NewVBox(false, 0)
op.window.Add(op.vbox) vbox := gtk.NewVBox(false, 0)
vbox.PackStart(op.table, false, true, 5)
vbox.PackStart(vert_sep_box, true, true, 5)
vbox.PackStart(buttons_box, false, true, 5)
op.window.Add(vbox)
op.window.ShowAll() op.window.ShowAll()
} }
@@ -244,26 +262,23 @@ func (op *OptionsProfile) InitializeUpdate(profile_name string) {
op.Initialize(true) op.Initialize(true)
// Get profile data. // Get profile data.
profile := []datamodels.Profile{} profile := ctx.Cache.Profiles[profile_name].Profile
err := ctx.Database.Db.Select(&profile, ctx.Database.Db.Rebind("SELECT * FROM urt_profiles WHERE name=?"), profile_name) op.profile_name.SetText(profile.Name)
if err != nil { op.binary_path.SetText(profile.Binary)
fmt.Println(err.Error()) op.additional_parameters.SetText(profile.Additional_params)
} if profile.Second_x_session == "1" {
op.profile_name.SetText(profile[0].Name)
op.binary_path.SetText(profile[0].Binary)
op.additional_parameters.SetText(profile[0].Additional_params)
if profile[0].Second_x_session == "1" {
op.another_x_session.SetActive(true) op.another_x_session.SetActive(true)
} }
if profile[0].Version == "4.3.0" { if profile.Version == "4.3.0" {
op.urt_version_combo.SetActive(1) op.urt_version_combo.SetActive(1)
} else if profile.Version == "4.3.1" {
op.urt_version_combo.SetActive(2)
} else { } else {
op.urt_version_combo.SetActive(0) op.urt_version_combo.SetActive(0)
} }
op.old_profile = &profile[0] op.old_profile = profile
} }