34 Commits

Author SHA1 Message Date
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 682 additions and 143 deletions

View File

@@ -25,6 +25,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 +36,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-beta5"
) )

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"
@@ -35,6 +35,7 @@ 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 = 100
_ = 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")
} }

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

@@ -121,7 +121,7 @@ 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
@@ -160,17 +160,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 +208,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 +234,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 +256,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")
@@ -301,7 +324,6 @@ func (m *MainWindow) hideOfflineFavoriteServers() {
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 +336,7 @@ 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) m.all_servers_store.Remove(iter)
server.AllServersIterInList = false server.AllServersIterInList = false
} }
@@ -336,7 +358,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)
} }
@@ -401,7 +423,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)
} }
@@ -437,6 +459,12 @@ 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."})
} }
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) {
fmt.Println("Setting toolbar's label text...") fmt.Println("Setting toolbar's label text...")
if strings.Contains(data["text"], "<markup>") { if strings.Contains(data["text"], "<markup>") {

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")
@@ -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(32, 32, gdkpixbuf.INTERP_NEAREST))
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)
@@ -173,6 +175,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)
} }
@@ -351,13 +354,30 @@ 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.
@@ -420,7 +440,18 @@ 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. // ToDo: remembering it to configuration storage.
m.all_servers_store_sortable.SetSortColumnId(m.column_pos["Servers"]["Name"], gtk.SORT_ASCENDING) // For some reason this cause panic on Windows, so disabling
// default sorting here.
if runtime.GOOS != "windows" {
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 +465,15 @@ 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)
}
} }
// Final separator. // Final separator.
@@ -473,7 +511,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 +546,15 @@ 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)
}
} }
// Final separator. // Final separator.
@@ -522,14 +571,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_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 all tabs") button_update_all_servers.SetTooltipText("Update all servers in all tabs")
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 +596,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 := ctx.Colorizer.ClearFromMarkup(name1_raw.GetString())
name2 := 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()
} }

View File

@@ -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()
} }
} }
} }
@@ -159,7 +177,7 @@ func (op *OptionsProfile) Initialize(update bool) {
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) urt_version_hbox.PackStart(urt_version_label, false, true, 5)
urt_version_hbox.PackStart(urt_version_sep, true, true, 5) urt_version_hbox.PackStart(urt_version_sep, true, true, 5)
urt_version_hbox.PackStart(op.urt_version_combo, true, true, 5) urt_version_hbox.PackStart(op.urt_version_combo, true, true, 5)