Secure access to the DERO network. https://dero.io
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2669 lines
90KB

  1. // Copyright 2020 DERO Foundation. All rights reserved.
  2. // build win: -ldflags -H=windowsgui
  3. // TODO: Code cleanup, seed languages, multi-language support, daemon/miner integration.
  4. package main
  5. import (
  6. "encoding/hex"
  7. "encoding/json"
  8. "github.com/deroproject/derosuite/address"
  9. "github.com/deroproject/derosuite/crypto"
  10. "github.com/deroproject/derosuite/globals"
  11. "github.com/deroproject/derosuite/transaction"
  12. "github.com/deroproject/derosuite/walletapi"
  13. rl "github.com/DankFC/raylib-goplus/raylib"
  14. "github.com/blang/semver"
  15. "io/ioutil"
  16. log "github.com/sirupsen/logrus"
  17. "os"
  18. "os/exec"
  19. "path/filepath"
  20. "runtime"
  21. "strconv"
  22. "strings"
  23. "time"
  24. )
  25. type _Keys struct {
  26. Spendkey_Secret crypto.Key `json:"spendkey_secret"`
  27. Spendkey_Public crypto.Key `json:"spendkey_public"`
  28. Viewkey_Secret crypto.Key `json:"viewkey_secret"`
  29. Viewkey_Public crypto.Key `json:"viewkey_public"`
  30. }
  31. type Config struct {
  32. RemoteDaemon string `json:"RemoteDaemon"`
  33. RemoteDaemonTestnet string `json:"RemoteDaemonTestnet"`
  34. LocalDaemon string `json:"LocalDaemon"`
  35. LocalDaemonTestnet string `json:"LocalDaemonTestnet"`
  36. RPCAuth string `json:"RPCAuth"`
  37. RPCAddress string `json:"RPCAddress"`
  38. DefaultMode string `json:"DefaultMode"`
  39. DefaultNetwork string `json:"DefaultNetwork"`
  40. }
  41. type Session struct {
  42. Path string
  43. Mode string // network mode (remote, local, offline)
  44. Syncing bool
  45. Network string
  46. Daemon string
  47. Rescan bool
  48. RPCServer bool
  49. RPCAuth string
  50. RPCAddress string
  51. Color rl.Color
  52. }
  53. type Transfer struct {
  54. rAddress *address.Address
  55. PaymentID []byte
  56. Amount uint64
  57. Fees uint64
  58. TX *transaction.Transaction
  59. TXID crypto.Hash
  60. Size float32
  61. Status string
  62. Inputs []uint64
  63. InputSum uint64
  64. Change uint64
  65. Relay bool
  66. OfflineTX bool
  67. Filename string
  68. }
  69. type Record struct {
  70. txNumber uint64
  71. txTime string
  72. txHeight string
  73. txTopoHeight string
  74. txID string
  75. txPaymentID string
  76. txAmount string
  77. txKey string
  78. txColor rl.Color
  79. }
  80. // Constants
  81. const windowWidth = 1600
  82. const windowHeight = 800
  83. const TARGET_FPS = 60
  84. const APP_PATH = ""
  85. const ASSET_PATH = "assets/"
  86. const FONT_PATH = "assets/fonts/"
  87. const DEFAULT_REMOTE_NODE = "http://rwallet.dero.live:20206"
  88. const DEFAULT_LOCAL_NODE = "http://127.0.0.1:20206"
  89. const DEFAULT_REMOTE_NODE_TESTNET = "http://explorer.dero.io:30306"
  90. const DEFAULT_LOCAL_NODE_TESTNET = "http://127.0.0.1:30306"
  91. const DEFAULT_RPC_ADDRESS = "127.0.0.1:20209"
  92. const DEFAULT_RPC_ADDRESS_TESTNET = "127.0.0.1:30309"
  93. const MAX_PW_LENGTH = 30
  94. // Globals
  95. var Version = semver.MustParse("0.3.0-3.Alpha")
  96. var config Config
  97. var wallet *walletapi.Wallet
  98. var account = &walletapi.Account{} // all account data is available here
  99. var session Session
  100. var transfer Transfer
  101. var daemonOnline bool
  102. var start int = 0
  103. var wHeight uint64
  104. var dHeight uint64
  105. var networkStatus bool
  106. var isFullscreen bool = false
  107. var drawFPS = false
  108. var spacing = float32(0)
  109. var lineSpacing = float32(0)
  110. var progressBar rl.Rectangle
  111. var progressTotal int
  112. var progressNow float32 = 0
  113. var fontSize float32 = 20
  114. var statusBar rl.Rectangle
  115. var command string
  116. var em bool
  117. var sidebar bool = false
  118. var sidebarText string = ""
  119. var createAccountCompleted bool = false
  120. var seed string
  121. var address_s string
  122. var statusText string = "Ready"
  123. var walletText string = ""
  124. var walletTextColor rl.Color = rl.White
  125. var statusBarX float32 = 0
  126. var statusBarY float32
  127. var statusBarWidth float32
  128. var statusColor rl.Color = rl.White
  129. var currentTime = time.Now()
  130. var currentTimeWidth rl.Vector2
  131. var windowIndex float32 = 0
  132. var availableText string = ""
  133. var pendingText string = ""
  134. var createAccountFilename string = ""
  135. var createAccountFilenameEditable bool = false
  136. var createAccountPassword string = ""
  137. var createAccountPasswordEditable bool = false
  138. var currentAccountPassword string
  139. var tempPasswordEditable bool = true
  140. var tempPasswordUpdated bool = false
  141. var tempPassword string = ""
  142. var files string
  143. var dropboxEdit bool = false
  144. var dropboxActive int = 0
  145. var dropboxOld int = 0
  146. var loginPassword string = ""
  147. var loginPasswordEditable bool = true
  148. var restorePassword string = ""
  149. var restorePasswordEditable bool = true
  150. var restoreFilename string = ""
  151. var restoreFilenameEditable bool = true
  152. var restoreHex string = ""
  153. var rescanHeight uint64
  154. var rescanPath string
  155. var rescanString string
  156. var amountString string
  157. var pidString string
  158. var receiverString string
  159. var rescanEditable bool = true
  160. var rescanUpdated bool = false
  161. var fileError bool = false
  162. var passwordError bool = false
  163. var restoreHexEditable bool = true
  164. var finalHex string
  165. var err error
  166. var src rl.Rectangle
  167. var dst rl.Rectangle
  168. var active int
  169. var checked bool = false
  170. var cmdGray rl.Color
  171. var cmdBlue rl.Color
  172. var cmdGreen rl.Color
  173. var transparent rl.Color
  174. // Main program loop
  175. func main() {
  176. // Set up logger
  177. log_file, err := os.OpenFile(APP_PATH + "logs.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
  178. if err != nil {
  179. log.Fatal(err)
  180. }
  181. defer log_file.Close()
  182. log.SetOutput(log_file)
  183. log.SetFormatter(&log.TextFormatter{})
  184. log.SetLevel(log.WarnLevel)
  185. // Map arguments
  186. globals.Arguments = make(map[string]interface{})
  187. globals.Arguments["--debug"] = false
  188. rl.SetConfigFlags(rl.FlagWindowResizable)
  189. rl.InitWindow(windowWidth, windowHeight, "CMD")
  190. rl.SetWindowMinSize(windowWidth, windowHeight)
  191. rl.SetWindowIcon(*rl.LoadImage(ASSET_PATH + "images/icon.png"))
  192. icon := *rl.LoadImage(ASSET_PATH + "images/nav.png")
  193. rl.SetWindowIcon(icon)
  194. rl.SetTargetFPS(60)
  195. rl.SetExitKey(0) // Disable ESC key behavior
  196. loadScreenDuration := float32(0)
  197. loadScreen := rl.LoadTexture(ASSET_PATH + "images/load.png")
  198. bg := rl.LoadTexture(ASSET_PATH + "images/background_1.png")
  199. tg := rl.LoadTexture(ASSET_PATH + "images/background_x.png")
  200. loadScreenColor := rl.White
  201. logo := rl.LoadTexture(ASSET_PATH + "images/nav.png")
  202. dero := rl.LoadTexture(ASSET_PATH + "images/dero.png")
  203. font := *rl.LoadFontEx(FONT_PATH + "ebrima.ttf", int(fontSize), nil, 256)
  204. fontMenu := *rl.LoadFontEx(FONT_PATH + "Raleway.ttf", int(40), nil, 256)
  205. fontHeader := *rl.LoadFontEx(FONT_PATH + "Raleway.ttf", int(30), nil, 256)
  206. fontSubHeader := *rl.LoadFontEx(FONT_PATH + "Raleway.ttf", int(25), nil, 256)
  207. fontBalance := *rl.LoadFontEx(FONT_PATH + "Electrolize.ttf", int(55), nil, 256)
  208. fontPending := *rl.LoadFontEx(FONT_PATH + "Electrolize.ttf", int(20), nil, 256)
  209. fontPassword := *rl.LoadFontEx(FONT_PATH + "Raleway.ttf", int(50), nil, 256)
  210. cmdGray = rl.NewColor(9, 17, 24, 255)
  211. cmdBlue = rl.NewColor(0, 210, 255, 255)
  212. cmdGreen = rl.NewColor(22, 238, 88, 255)
  213. transparent = rl.NewColor(0,0,0,0)
  214. rl.GuiSetFont(fontSubHeader)
  215. session.Color = cmdGreen
  216. session.Network = "Mainnet"
  217. // Check/load config
  218. if _, err = os.Stat(APP_PATH + "config.json"); err != nil {
  219. data := Config{
  220. RemoteDaemon: DEFAULT_REMOTE_NODE,
  221. RemoteDaemonTestnet: DEFAULT_REMOTE_NODE_TESTNET,
  222. LocalDaemon: DEFAULT_LOCAL_NODE,
  223. LocalDaemonTestnet: DEFAULT_LOCAL_NODE_TESTNET,
  224. RPCAuth: "",
  225. RPCAddress: "127.0.0.1:20209",
  226. DefaultMode: "remote",
  227. DefaultNetwork: "Mainnet",
  228. }
  229. file, _ := json.MarshalIndent(data, "", " ")
  230. _ = ioutil.WriteFile("config.json", file, 0644)
  231. session.Mode = "remote"
  232. session.Daemon = DEFAULT_REMOTE_NODE
  233. session.Rescan = false
  234. session.Syncing = false
  235. session.Network = "Mainnet"
  236. session.RPCServer = false
  237. session.RPCAddress = "127.0.0.1:20209"
  238. config.RemoteDaemon = DEFAULT_REMOTE_NODE
  239. config.RemoteDaemonTestnet = DEFAULT_REMOTE_NODE_TESTNET
  240. config.LocalDaemon = DEFAULT_LOCAL_NODE
  241. config.LocalDaemonTestnet = DEFAULT_LOCAL_NODE_TESTNET
  242. config.DefaultMode = "remote"
  243. config.DefaultNetwork = "Mainnet"
  244. config.RPCAddress = "127.0.0.1:20209"
  245. globals.Arguments["--testnet"] = false
  246. log.Warnf("File not found, config.json created.")
  247. } else {
  248. file, err := os.Open(APP_PATH + "config.json")
  249. if err == nil {
  250. defer file.Close()
  251. byteValue, _ := ioutil.ReadAll(file)
  252. json.Unmarshal(byteValue, &config)
  253. session.Network = config.DefaultNetwork
  254. if session.Network == "Mainnet" {
  255. if config.DefaultMode == "remote" {
  256. session.Daemon = config.RemoteDaemon
  257. session.Mode = "remote"
  258. } else if config.DefaultMode == "local" {
  259. session.Daemon = config.LocalDaemon
  260. session.Mode = "local"
  261. } else if config.DefaultMode == "" {
  262. session.Daemon = DEFAULT_REMOTE_NODE
  263. session.Mode = "remote"
  264. } else {
  265. session.Daemon = ""
  266. session.Mode = "offline"
  267. }
  268. session.RPCAddress = "127.0.0.1:20209"
  269. globals.Arguments["--testnet"] = false
  270. } else {
  271. if config.DefaultMode == "remote" {
  272. session.Daemon = config.RemoteDaemonTestnet
  273. session.Mode = "remote"
  274. } else if config.DefaultMode == "local" {
  275. session.Daemon = config.LocalDaemonTestnet
  276. session.Mode = "local"
  277. } else if config.DefaultMode == "" {
  278. session.Daemon = DEFAULT_REMOTE_NODE_TESTNET
  279. session.Mode = "remote"
  280. } else {
  281. session.Daemon = ""
  282. session.Mode = "offline"
  283. }
  284. session.RPCAddress = "127.0.0.1:30309"
  285. globals.Arguments["--testnet"] = true
  286. }
  287. session.Rescan = false
  288. session.Syncing = false
  289. session.RPCServer = false
  290. } else {
  291. session.Network = "Mainnet"
  292. session.Mode = "remote"
  293. session.Daemon = DEFAULT_REMOTE_NODE
  294. session.Rescan = false
  295. session.Syncing = false
  296. session.Network = "Mainnet"
  297. session.RPCServer = false
  298. session.RPCAddress = "127.0.0.1:20209"
  299. globals.Arguments["--testnet"] = false
  300. log.Warnf("Error loading config.json, loading default settings.")
  301. }
  302. }
  303. if session.Network == "Mainnet" {
  304. session.Color = cmdGreen
  305. } else {
  306. session.Color = cmdBlue
  307. }
  308. globals.Initialize()
  309. for !rl.WindowShouldClose() {
  310. reloadConfig()
  311. rl.GuiSetStyle(0, 1, rl.ColorToInt(cmdGray))
  312. rl.GuiSetStyle(0, 2, rl.ColorToInt(session.Color))
  313. rl.GuiSetStyle(0, 3, rl.ColorToInt(session.Color))
  314. rl.GuiSetStyle(0, 4, rl.ColorToInt(cmdGray))
  315. rl.GuiSetStyle(0, 5, rl.ColorToInt(session.Color)) // label fore color
  316. rl.GuiSetStyle(0, 6, rl.ColorToInt(session.Color)) // button text border
  317. rl.GuiSetStyle(0, 7, rl.ColorToInt(cmdGray))
  318. rl.GuiSetStyle(0, 8, rl.ColorToInt(session.Color)) // textbox active background
  319. rl.GuiSetStyle(0, 9, rl.ColorToInt(cmdGray))
  320. rl.GuiSetStyle(1, 1, rl.ColorToInt(cmdGray))
  321. rl.GuiSetStyle(1, 2, rl.ColorToInt(cmdGray))
  322. rl.GuiSetStyle(1, 3, rl.ColorToInt(cmdGray))
  323. rl.GuiSetStyle(1, 4, rl.ColorToInt(session.Color))
  324. rl.GuiSetStyle(1, 5, rl.ColorToInt(session.Color))
  325. rl.GuiSetStyle(1, 6, rl.ColorToInt(cmdGray))
  326. rl.GuiSetStyle(1, 7, rl.ColorToInt(cmdGray))
  327. rl.GuiSetStyle(1, 8, rl.ColorToInt(cmdGray))
  328. rl.GuiSetStyle(1, 9, rl.ColorToInt(cmdGray))
  329. rl.GuiSetStyle(2, 1, rl.ColorToInt(cmdGray))
  330. rl.GuiSetStyle(2, 2, rl.ColorToInt(session.Color))
  331. rl.GuiSetStyle(2, 3, rl.ColorToInt(session.Color))
  332. rl.GuiSetStyle(2, 4, rl.ColorToInt(cmdGray))
  333. rl.GuiSetStyle(2, 5, rl.ColorToInt(session.Color))
  334. rl.GuiSetStyle(2, 6, rl.ColorToInt(session.Color))
  335. rl.GuiSetStyle(2, 7, rl.ColorToInt(session.Color))
  336. rl.GuiSetStyle(2, 8, rl.ColorToInt(session.Color))
  337. rl.GuiSetStyle(2, 9, rl.ColorToInt(session.Color))
  338. rl.GuiSetStyle(3, 1, rl.ColorToInt(cmdGray))
  339. rl.GuiSetStyle(3, 2, rl.ColorToInt(session.Color))
  340. rl.GuiSetStyle(3, 3, rl.ColorToInt(session.Color))
  341. rl.GuiSetStyle(3, 4, rl.ColorToInt(session.Color))
  342. rl.GuiSetStyle(3, 5, rl.ColorToInt(session.Color))
  343. rl.GuiSetStyle(3, 6, rl.ColorToInt(session.Color))
  344. rl.GuiSetStyle(3, 7, rl.ColorToInt(session.Color))
  345. rl.GuiSetStyle(3, 8, rl.ColorToInt(session.Color))
  346. rl.GuiSetStyle(3, 9, rl.ColorToInt(session.Color))
  347. rl.GuiSetStyle(rl.GuiControlLabel, rl.GuiPropertyTextColorNormal, rl.ColorToInt(rl.White))
  348. rl.GuiSetStyle(rl.GuiControlButton, rl.GuiPropertyTextColorNormal, rl.ColorToInt(rl.White))
  349. rl.GuiSetStyle(rl.GuiControlButton, rl.GuiPropertyBaseColorNormal, rl.ColorToInt(cmdGray))
  350. rl.GuiSetStyle(rl.GuiControlDropDownBox, rl.GuiPropertyTextColorNormal, rl.ColorToInt(session.Color))
  351. rl.GuiSetStyle(rl.GuiControlDropDownBox, rl.GuiPropertyBaseColorNormal, rl.ColorToInt(cmdGray))
  352. if rl.IsKeyPressed(rl.KeyEscape) {
  353. if (isFullscreen == true) {
  354. rl.SetWindowSize(windowWidth, windowHeight)
  355. isFullscreen = false
  356. statusBar = rl.Rectangle{0, float32(rl.GetScreenWidth()) - 32, float32(rl.GetScreenHeight()), 32}
  357. statusBarY = float32(rl.GetScreenHeight()) - 30
  358. statusBarWidth = float32(rl.GetScreenWidth())
  359. rl.ToggleFullscreen()
  360. rl.SetWindowPosition(60, 40)
  361. }
  362. }
  363. if rl.IsKeyPressed(rl.KeyF1) {
  364. if (isFullscreen == false) {
  365. rl.SetWindowSize(rl.GetMonitorWidth(0), rl.GetMonitorHeight(0))
  366. isFullscreen = true
  367. rl.ToggleFullscreen()
  368. statusBar = rl.Rectangle{0, float32(rl.GetMonitorHeight(0)) - 32, float32(rl.GetMonitorWidth(0)), 32}
  369. } else {
  370. rl.SetWindowSize(windowWidth, windowHeight)
  371. isFullscreen = false
  372. statusBar = rl.Rectangle{0, float32(rl.GetScreenWidth()) - 32, float32(rl.GetScreenHeight()), 32}
  373. statusBarY = float32(rl.GetScreenHeight()) - 30
  374. statusBarWidth = float32(rl.GetScreenWidth())
  375. rl.ToggleFullscreen()
  376. rl.SetWindowPosition(60, 40)
  377. }
  378. }
  379. rl.ClearBackground(rl.Black)
  380. rl.BeginDrawing()
  381. color := rl.White
  382. color.A = 200
  383. color = rl.White
  384. statusBG := rl.Black
  385. statusBG.A = 60
  386. rect80 := rl.Black
  387. rect80.A = 95
  388. terminalBG := rl.Black
  389. terminalBG.A = 250
  390. loadScreenDuration += rl.GetFrameTime()
  391. if loadScreenDuration >= 3.5 {
  392. if loadScreenColor.A > 5 {
  393. loadScreenColor.A -= 5
  394. } else {
  395. loadScreenColor.A = 0
  396. }
  397. }
  398. statusBar = rl.Rectangle{0, float32(rl.GetScreenHeight()) - 32, float32(rl.GetScreenWidth()), 32}
  399. progressBar = rl.Rectangle{0, float32(rl.GetScreenHeight()) - 36, float32(progressNow), 4}
  400. currentTime := time.Now().Format(time.UnixDate)
  401. currentTimeWidth = rl.MeasureTextEx(font, currentTime, fontSize, spacing)
  402. src = rl.Rectangle{0, 0, float32(loadScreen.Width), float32(loadScreen.Height)}
  403. dst = rl.Rectangle{0, 0, float32(rl.GetScreenWidth()), float32(rl.GetScreenHeight())}
  404. rl.DrawTexturePro(loadScreen, src, dst, rl.Vector2{0,0}, 0, loadScreenColor)
  405. statusBarY = float32(rl.GetScreenHeight()) - 30
  406. statusBarWidth = float32(rl.GetScreenWidth())
  407. offsetX := float32(300)
  408. if loadScreenColor.A == 0 {
  409. syncStatus()
  410. // Background
  411. src := rl.Rectangle{0, 0, float32(bg.Width), float32(bg.Height)}
  412. src2 := rl.Rectangle{0, 0, float32(tg.Width), float32(tg.Height)}
  413. dst := rl.Rectangle{0, 0, float32(rl.GetScreenWidth()), float32(rl.GetScreenHeight() + 30)}
  414. if windowIndex == 0 {
  415. rl.DrawTexturePro(tg, src2, dst, rl.Vector2{0,0}, 0, rl.White)
  416. } else {
  417. rl.DrawTexturePro(bg, src, dst, rl.Vector2{0,30}, 0, rl.White)
  418. }
  419. // Status Bar
  420. rl.DrawRectangleRec(statusBar, statusBG)
  421. rl.DrawRectangleRec(progressBar, session.Color)
  422. rl.DrawLine(int(statusBarX), int(statusBarY - 1), int(statusBarX + statusBarWidth), int(statusBarY - 1), rl.DarkGray)
  423. rl.DrawTextEx(font, "--" + session.Network + "-- " + statusText + "" + walletText, rl.Vector2{25, statusBarY + 3}, fontSize, spacing, statusColor)
  424. rl.DrawTextEx(font, currentTime, rl.Vector2{float32(rl.GetScreenWidth()) - currentTimeWidth.X - 25, statusBarY + 3}, fontSize, spacing, rl.White)
  425. // Logo
  426. rl.GuiSetStyle(rl.GuiControlButton, rl.GuiPropertyBorderColorNormal, 00000000)
  427. rl.GuiSetStyle(rl.GuiControlButton, rl.GuiPropertyBorderColorFocused, 00000000)
  428. rl.DrawTexture(logo, 30, 0, session.Color)
  429. rl.GuiSetStyle(rl.GuiControlButton, rl.GuiPropertyBorderColorNormal, rl.ColorToInt(rl.Gray))
  430. rl.GuiSetStyle(rl.GuiControlButton, rl.GuiPropertyBorderColorFocused, rl.ColorToInt(session.Color))
  431. if windowIndex < 2.2 {
  432. if rl.GuiLabelButton(rl.NewRectangle(30, 150, 40, 40), "Create") {
  433. if wallet == nil {
  434. windowIndex = 1.0
  435. }
  436. }
  437. if rl.GuiLabelButton(rl.NewRectangle(30, 200, 40, 40), "View") {
  438. if wallet == nil {
  439. windowIndex = 2.0
  440. } else {
  441. windowIndex = 2.2
  442. }
  443. }
  444. if rl.GuiLabelButton(rl.NewRectangle(30, 250, 40, 40), "Restore") {
  445. if wallet == nil {
  446. restoreFilename = ""
  447. restorePassword = ""
  448. restoreHex = ""
  449. fileError = false
  450. windowIndex = 3.0
  451. }
  452. }
  453. if rl.GuiLabelButton(rl.NewRectangle(30, float32(rl.GetScreenHeight() - 120), 40, 40), "Settings") {
  454. if wallet == nil {
  455. windowIndex = 4.0
  456. }
  457. }
  458. } else if windowIndex < 3.0 {
  459. if rl.GuiLabelButton(rl.NewRectangle(30, 150, 40, 40), "Overview") {
  460. if wallet != nil {
  461. windowIndex = 2.2
  462. }
  463. }
  464. if rl.GuiLabelButton(rl.NewRectangle(30, 200, 40, 40), "Send") {
  465. if wallet != nil {
  466. windowIndex = 2.5
  467. }
  468. }
  469. if rl.GuiLabelButton(rl.NewRectangle(30, 250, 40, 40), "Receive") {
  470. if wallet != nil {
  471. windowIndex = 2.4
  472. }
  473. }
  474. if rl.GuiLabelButton(rl.NewRectangle(30, 300, 40, 40), "View History") {
  475. if wallet != nil {
  476. windowIndex = 2.6
  477. }
  478. }
  479. if rl.GuiLabelButton(rl.NewRectangle(30, 350, 40, 40), "Rescan") {
  480. if wallet != nil {
  481. windowIndex = 2.3
  482. }
  483. }
  484. if rl.GuiLabelButton(rl.NewRectangle(30, 400, 40, 40), "Options") {
  485. if wallet != nil {
  486. windowIndex = 2.7
  487. }
  488. }
  489. if rl.GuiLabelButton(rl.NewRectangle(30, float32(rl.GetScreenHeight() - 120), 40, 40), "Log Out") {
  490. closeWallet()
  491. }
  492. } else if (windowIndex >= 3.0 && windowIndex < 4.0) {
  493. if rl.GuiLabelButton(rl.NewRectangle(30, 150, 40, 40), "Create") {
  494. if wallet == nil {
  495. windowIndex = 1.0
  496. }
  497. }
  498. if rl.GuiLabelButton(rl.NewRectangle(30, 200, 40, 40), "View") {
  499. if wallet == nil {
  500. windowIndex = 2.0
  501. } else {
  502. windowIndex = 2.2
  503. }
  504. }
  505. if rl.GuiLabelButton(rl.NewRectangle(30, 250, 40, 40), "Restore") {
  506. if wallet == nil {
  507. restoreFilename = ""
  508. restorePassword = ""
  509. restoreHex = ""
  510. fileError = false
  511. windowIndex = 3.0
  512. }
  513. }
  514. if rl.GuiLabelButton(rl.NewRectangle(30, float32(rl.GetScreenHeight() - 120), 40, 40), "Settings") {
  515. windowIndex = 4.0
  516. }
  517. } else if (windowIndex >= 4.0 && windowIndex < 5.0) {
  518. if rl.GuiLabelButton(rl.NewRectangle(30, 150, 40, 40), "Network") {
  519. windowIndex = 4.1
  520. }
  521. if rl.GuiLabelButton(rl.NewRectangle(30, 200, 40, 40), "Network Mode") {
  522. windowIndex = 4.2
  523. }
  524. if rl.GuiLabelButton(rl.NewRectangle(30, 250, 40, 40), "Daemon") {
  525. windowIndex = 4.3
  526. }
  527. if rl.GuiLabelButton(rl.NewRectangle(30, 300, 40, 40), "RPC Server") {
  528. windowIndex = 4.4
  529. }
  530. if rl.GuiLabelButton(rl.NewRectangle(30, 350, 40, 40), "Version") {
  531. windowIndex = 4.5
  532. }
  533. if rl.GuiLabelButton(rl.NewRectangle(30, float32(rl.GetScreenHeight() - 120), 40, 40), "Back") {
  534. windowIndex = 0
  535. }
  536. }
  537. switch windowIndex {
  538. case 0:
  539. clearVars()
  540. statusColor = rl.White
  541. statusText = "Ready"
  542. break
  543. // Create an account
  544. case 1.0:
  545. if fileError == true {
  546. rl.DrawTextEx(font, "That account name already exists.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  547. }
  548. createAccountCompleted = false
  549. statusColor = rl.White
  550. statusText = "Ready"
  551. seed = ""
  552. address_s = ""
  553. passwordError = false
  554. createAccountFilenameEditable = true
  555. rl.DrawTextEx(fontMenu, "Create an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  556. rl.DrawTextEx(font, "/ Account Name /", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  557. rl.DrawTextEx(fontSubHeader, "Account Name: (Example: Expenses)", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  558. _, createAccountFilename = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), createAccountFilename, 60, createAccountFilenameEditable)
  559. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  560. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  561. createAccountFilename = strings.Trim(createAccountFilename, ".")
  562. createAccountFilename = strings.Trim(createAccountFilename, " ")
  563. masked, mask := textMask(createAccountFilename)
  564. if masked {
  565. rl.DrawTextEx(fontHeader, mask, rl.Vector2{offsetX, 231}, 30, spacing, session.Color)
  566. }
  567. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  568. if createAccountFilename == "" {
  569. break
  570. }
  571. if _, err = os.Stat(APP_PATH + createAccountFilename + ".db"); err != nil {
  572. fileError = false
  573. windowIndex = 1.1
  574. } else {
  575. createAccountFilename = ""
  576. fileError = true
  577. }
  578. }
  579. break
  580. case 1.1:
  581. if passwordError == true {
  582. rl.DrawTextEx(font, "The provided passwords do not match.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  583. }
  584. createAccountPasswordEditable = true
  585. rl.DrawTextEx(fontMenu, "Create an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  586. rl.DrawTextEx(font, "/ Account Name / Password /", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  587. rl.DrawTextEx(fontSubHeader, "Account Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  588. _, createAccountPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), createAccountPassword, 160, createAccountPasswordEditable)
  589. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  590. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  591. masked, mask := passwordMask(createAccountPassword)
  592. if masked {
  593. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  594. }
  595. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  596. if createAccountPassword != "" {
  597. windowIndex = 1.12
  598. }
  599. }
  600. break
  601. case 1.12:
  602. tempPasswordEditable = true
  603. rl.DrawTextEx(fontMenu, "Create an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  604. rl.DrawTextEx(font, "/ Account Name / Password / Confirm Password /", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  605. rl.DrawTextEx(fontSubHeader, "Confirm Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  606. _, tempPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), tempPassword, 160, tempPasswordEditable)
  607. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  608. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  609. masked, mask := passwordMask(tempPassword)
  610. if masked {
  611. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  612. }
  613. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Submit") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  614. if (len(createAccountPassword) < 1) {
  615. } else {
  616. if tempPassword == createAccountPassword {
  617. windowIndex = 1.2
  618. } else {
  619. passwordError = true
  620. createAccountPassword = ""
  621. tempPassword = ""
  622. windowIndex = 1.1
  623. }
  624. }
  625. }
  626. break
  627. case 1.2:
  628. if createAccountCompleted == false {
  629. wallet, err := walletapi.Create_Encrypted_Wallet_Random(createAccountFilename + ".db", createAccountPassword)
  630. if err != nil {
  631. wallet = nil
  632. windowIndex = 1.0
  633. log.Warnf("Error creating wallet: %s", err)
  634. // TODO: Add error-feedback for the user.
  635. } else {
  636. err = wallet.Set_Encrypted_Wallet_Password(createAccountPassword)
  637. if err != nil {
  638. log.Warnf("Error changing password: %s", err)
  639. }
  640. // TODO wallet.Set_Seed_Language(language)
  641. session.Path = createAccountFilename + ".db"
  642. currentAccountPassword = createAccountPassword
  643. createAccountCompleted = true
  644. createAccountFilename = ""
  645. createAccountPassword = ""
  646. tempPassword = ""
  647. passwordError = false
  648. seed = wallet.GetSeed()
  649. address_s = wallet.GetAddress().String()
  650. wallet.Close_Encrypted_Wallet()
  651. }
  652. } else {
  653. rl.DrawTextEx(fontMenu, "Account Status", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  654. rl.DrawTextEx(font, "Please review the following account information.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  655. rl.DrawTextEx(fontHeader, "Your account address:", rl.Vector2{offsetX, 150}, 30, spacing, rl.White)
  656. rl.DrawTextEx(font, address_s, rl.Vector2{offsetX, 200}, 20, spacing, session.Color)
  657. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 300, 840, 150), rect80)
  658. rl.DrawTextRec(fontSubHeader, seed, rl.NewRectangle(offsetX + 20, 320, 800, 150), 25, spacing, true, rl.White)
  659. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 470, 840, 35), session.Color)
  660. rl.DrawTextEx(fontSubHeader, "Keep these recovery words safe, or you may lose your account forever.", rl.Vector2{offsetX + 10, 475}, 25, spacing, rl.Black)
  661. if rl.GuiButton(rl.NewRectangle(offsetX, float32(rl.GetScreenHeight() - 130), 250, 50), "Copy Seed") {
  662. rl.SetClipboardText(seed)
  663. }
  664. if rl.GuiButton(rl.NewRectangle(590, float32(rl.GetScreenHeight() - 130), 250, 50), "View Account") {
  665. windowIndex = 2.1
  666. }
  667. }
  668. break
  669. // View an account
  670. case 2.0:
  671. clearVars()
  672. statusColor = rl.White
  673. statusText = "Ready"
  674. passwordError = false
  675. if (wallet != nil && session.Path != "") {
  676. windowIndex = 2.2
  677. }
  678. fileCount := 0
  679. /*
  680. matches, _ := filepath.Glob(APP_PATH + "*")
  681. for _, match := range matches {
  682. check, _ := os.Stat(match)
  683. if !check.IsDir() {
  684. if strings.Contains(match, ".") {
  685. } else if match == "CMD" {
  686. } else {
  687. if fileCount > 0 {
  688. files += ";" + match
  689. fileCount += 1
  690. } else {
  691. files = match
  692. fileCount += 1
  693. }
  694. }
  695. }
  696. }
  697. */
  698. matches, _ := filepath.Glob(APP_PATH + "*.db")
  699. for _, match := range matches {
  700. check, _ := os.Stat(match)
  701. if !check.IsDir() {
  702. if strings.Contains(match, ".db") && !strings.Contains(match, ".lock") {
  703. if fileCount > 0 {
  704. files += ";" + match
  705. fileCount += 1
  706. } else {
  707. files = match
  708. fileCount += 1
  709. }
  710. }
  711. }
  712. }
  713. fileList := strings.Split(files, ";")
  714. if (fileCount < 1) {
  715. rl.DrawTextEx(fontHeader, "Oops, Looks like there are no accounts here.", rl.Vector2{offsetX, 150}, 30, spacing, rl.Orange)
  716. } else {
  717. loginPassword = ""
  718. rl.DrawTextEx(fontMenu, "Select an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  719. rl.DrawTextEx(font, "Choose from the previously created accounts below.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  720. if (rl.GuiButton(rl.NewRectangle(offsetX, 250, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  721. if (session.Path != "") {
  722. windowIndex = 2.1
  723. }
  724. }
  725. combo := rl.GuiComboBox(rl.NewRectangle(offsetX, 150, 600, 50), files, active)
  726. if combo != active {
  727. active = combo
  728. }
  729. session.Path = fileList[active]
  730. }
  731. break
  732. case 2.1:
  733. if passwordError == true {
  734. rl.DrawTextEx(font, "The password entered is incorrect.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  735. }
  736. rl.DrawTextEx(fontMenu, "Sign-in to an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  737. rl.DrawTextEx(font, "Enter your password below to gain access to your account.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  738. rl.DrawTextEx(fontHeader, session.Path, rl.Vector2{offsetX, 150}, 30, spacing, rl.White)
  739. _, loginPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), loginPassword, 60, loginPasswordEditable)
  740. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  741. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  742. masked, mask := passwordMask(loginPassword)
  743. if masked {
  744. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  745. }
  746. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Sign In") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  747. if loginPassword != "" {
  748. windowIndex = 2.2
  749. }
  750. }
  751. break
  752. case 2.2:
  753. if session.Path == "" {
  754. windowIndex = 2.0
  755. } else {
  756. if wallet == nil {
  757. wallet, err = walletapi.Open_Encrypted_Wallet(session.Path, loginPassword)
  758. if err != nil {
  759. loginPassword = ""
  760. passwordError = true
  761. windowIndex = 2.1
  762. log.Warnf("Error opening wallet: %s", err)
  763. break
  764. }
  765. }
  766. loginPassword = ""
  767. passwordError = false
  768. resetTransfer()
  769. wallet.SetDaemonAddress(session.Daemon)
  770. if session.Mode != "offline" {
  771. if (session.Rescan && session.Path == rescanPath) {
  772. session.Rescan = false
  773. rescan_bc(wallet, 0)
  774. session.Syncing = true
  775. } else {
  776. wallet.SetOnlineMode()
  777. session.Syncing = true
  778. }
  779. } else {
  780. wallet.SetOfflineMode()
  781. session.Syncing = false
  782. }
  783. var viewOnly string
  784. if wallet.Is_View_Only() {
  785. viewOnly = "(View-Only)"
  786. } else {
  787. viewOnly = ""
  788. }
  789. balance, pending := wallet.Get_Balance()
  790. rl.DrawTextEx(fontMenu, "Account Overview", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  791. rl.DrawTextEx(font, "Welcome Commander, here you can view the status of your active account, manage application settings, and more.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  792. rl.DrawTexture(dero, int(offsetX), 250, rl.White)
  793. rl.DrawTextEx(fontBalance, globals.FormatMoney8(balance) + pendingText, rl.Vector2{offsetX + 65, 250}, 55, spacing, rl.White)
  794. rl.DrawTextEx(font, "PENDING", rl.Vector2{offsetX + 68, 304}, 20, spacing, rl.White)
  795. rl.DrawTextEx(fontPending, globals.FormatMoney8(pending), rl.Vector2{offsetX + 140, 305}, 20, spacing, rl.White)
  796. rl.DrawTextEx(fontHeader, session.Path + " " + viewOnly , rl.Vector2{offsetX, 150}, 30, spacing, session.Color)
  797. rl.DrawTextEx(fontSubHeader, trim_address(wallet.GetAddress().String()), rl.Vector2{offsetX, 200}, 25, spacing, rl.White)
  798. available := true
  799. in := true
  800. out := true
  801. pool := true
  802. failed := false
  803. min_height := uint64(0)
  804. max_height := uint64(0)
  805. transfers := wallet.Show_Transfers(available, in, out, pool, failed, false, min_height, max_height)
  806. if len(transfers) == 0 {
  807. if (rl.GuiLabelButton(rl.NewRectangle(offsetX, 350, 200, 50), "Last Transaction")) {
  808. }
  809. rl.DrawTextEx(font, "No transaction history.", rl.Vector2{offsetX, 410}, 20, spacing, rl.Gray)
  810. } else {
  811. var record Record
  812. record.txNumber = 0
  813. record.txTime = string(transfers[record.txNumber].Time.Format(time.RFC822))
  814. record.txHeight = strconv.FormatUint(transfers[record.txNumber].Height, 10)
  815. record.txTopoHeight = strconv.FormatInt(transfers[record.txNumber].TopoHeight, 10)
  816. record.txID = transfers[record.txNumber].TXID.String()
  817. record.txPaymentID = string(transfers[record.txNumber].PaymentID)
  818. record.txAmount = globals.FormatMoney12(transfers[record.txNumber].Amount)
  819. //record.txKey = string(wallet.GetTXKey(crypto.HexToHash(record.txID)))
  820. record.txColor = rl.Gray
  821. switch transfers[record.txNumber].Status {
  822. case 0:
  823. record.txColor = session.Color
  824. rl.DrawTextEx(font, record.txTime + "\nTransaction ID: " + record.txID + "\nRECEIVED " + record.txAmount + "\n[ " + record.txHeight + " / " + record.txTopoHeight + " ]", rl.Vector2{offsetX + 30, 410}, 20, spacing, rl.White)
  825. break
  826. case 1:
  827. record.txColor = rl.Magenta
  828. rl.DrawTextEx(font, record.txTime + "\nTransaction ID: " + record.txID + "\nSPENT " + record.txAmount + "\n[ " + record.txHeight + " / " + record.txTopoHeight + " ]", rl.Vector2{offsetX + 30, 410}, 20, spacing, rl.White)
  829. break
  830. case 2:
  831. fallthrough
  832. default:
  833. record.txColor = rl.Gray
  834. rl.DrawTextEx(font, record.txTime + "\nTransaction ID: " + record.txID + "\nTransaction status unknown\n" + string(transfers[0].Status), rl.Vector2{offsetX + 30, 410}, 20, spacing, rl.White)
  835. }
  836. rl.DrawLineEx(rl.Vector2{offsetX + 5, 410}, rl.Vector2{offsetX + 5, 525}, 4.0, record.txColor)
  837. if (rl.GuiLabelButton(rl.NewRectangle(offsetX, 350, 200, 50), "Last Transaction")) {
  838. openURL("http://explorer.dero.io/tx/" + record.txID)
  839. }
  840. }
  841. rl.DrawLineEx(rl.Vector2{1100, 150}, rl.Vector2{1100, 525}, 1.0, rl.DarkGray)
  842. if rl.GuiLabelButton(rl.NewRectangle(1150, 145, 200, 50), "RPC Server") {
  843. if session.RPCServer == false {
  844. if session.Mode != "offline" {
  845. if session.Network == "Testnet" {
  846. err = wallet.Start_RPC_Server(DEFAULT_RPC_ADDRESS_TESTNET)
  847. session.RPCAddress = DEFAULT_RPC_ADDRESS_TESTNET
  848. } else {
  849. err = wallet.Start_RPC_Server(DEFAULT_RPC_ADDRESS)
  850. session.RPCAddress = DEFAULT_RPC_ADDRESS
  851. }
  852. if err == nil {
  853. session.RPCServer = true
  854. } else {
  855. log.Warnf("Error starting RPC server: %s", err)
  856. }
  857. }
  858. } else {
  859. session.RPCServer = false
  860. wallet.Stop_RPC_Server()
  861. }
  862. }
  863. if session.RPCServer == true {
  864. rl.DrawTextEx(font, "ONLINE @ " + session.RPCAddress, rl.Vector2{1150, 195}, 20, 0, session.Color)
  865. } else {
  866. rl.DrawTextEx(font, "OFFLINE", rl.Vector2{1150, 195}, 20, 0, rl.Gray)
  867. }
  868. modeText := ""
  869. modeColor := rl.Gray
  870. if session.Mode == "offline" {
  871. modeText = "OFFLINE"
  872. modeColor = rl.Gray
  873. } else {
  874. modeText = strings.ToUpper(session.Mode) + " @ " + session.Daemon
  875. modeColor = session.Color
  876. }
  877. if rl.GuiLabelButton(rl.NewRectangle(1150, 240, 200, 50), "Network Mode") {
  878. toggleMode()
  879. }
  880. rl.DrawTextEx(font, modeText, rl.Vector2{1150, 295}, 20, 0, modeColor)
  881. rl.DrawTextEx(fontSubHeader, "My Node", rl.Vector2{1150, 355}, 25, 0, rl.White)
  882. if session.Mode == "local" {
  883. if dHeight == 0 {
  884. rl.DrawTextEx(font, "N/A", rl.Vector2{1150, 395}, 20, 0, rl.Gray)
  885. } else {
  886. rl.DrawTextEx(font, "Daemon TopoHeight: " + strconv.FormatUint(dHeight, 10), rl.Vector2{1150, 395}, 20, 0, session.Color)
  887. }
  888. } else {
  889. rl.DrawTextEx(font, "N/A", rl.Vector2{1150, 395}, 20, 0, rl.Gray)
  890. }
  891. rl.DrawTextEx(fontSubHeader, "Sync Status", rl.Vector2{1150, 455}, 25, 0, rl.White)
  892. status := "Checking..."
  893. percent := float64(0)
  894. statusColor := rl.Gray
  895. diff := int64(0)
  896. if (wHeight != 0 && dHeight != 0) {
  897. if int64(wHeight) <= int64(dHeight) {
  898. diff = int64(dHeight) - int64(wHeight)
  899. percent = (float64(wHeight) / float64(dHeight)) * 100
  900. percentS := strconv.FormatFloat(percent, 'f', 1, 64)
  901. if (diff > 5 && wHeight != 0) {
  902. status = "Syncing... " + percentS + "%"
  903. statusColor = rl.Gray
  904. } else if diff < 5 {
  905. status = "Syncing Complete."
  906. statusColor = session.Color
  907. } else {
  908. status = "Syncing..."
  909. statusColor = rl.Gray
  910. }
  911. } else {
  912. status = "Checking..."
  913. }
  914. }
  915. if session.Mode == "offline" {
  916. status = "N/A"
  917. }
  918. rl.DrawTextEx(font, status, rl.Vector2{1150, 495}, 20, 0, statusColor)
  919. // Toggle Offline Mode
  920. if session.Mode == "offline" {
  921. if rl.GuiButton(rl.NewRectangle(float32(rl.GetScreenWidth() - 150), 30, 120, 50), "Offline") {
  922. session.Mode = "remote"
  923. session.Daemon = config.RemoteDaemon
  924. wallet.SetOnlineMode()
  925. session.Syncing = true
  926. }
  927. } else {
  928. if rl.GuiButton(rl.NewRectangle(float32(rl.GetScreenWidth() - 150), 30, 120, 50), "Online") {
  929. session.Mode = "offline"
  930. session.RPCServer = false
  931. wallet.Stop_RPC_Server()
  932. wallet.SetOfflineMode()
  933. session.Syncing = false
  934. wallet.Close_Encrypted_Wallet()
  935. wallet = nil
  936. windowIndex = 2.1
  937. }
  938. }
  939. }
  940. break
  941. // Rescan options
  942. case 2.3:
  943. if wallet == nil {
  944. windowIndex = 2.0
  945. break
  946. }
  947. rl.DrawTextEx(fontMenu, "Rescan Settings", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  948. rl.DrawTextEx(font, "Rescan the entire blockchain. This process can take longer than an hour to complete.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  949. if rl.GuiButton(rl.NewRectangle(offsetX, 150, 200, 50), "Start Rescan") {
  950. session.Rescan = true
  951. rescanPath = session.Path
  952. rescanHeight = stoUint64(rescanString)
  953. wallet.SetOfflineMode()
  954. session.Syncing = false
  955. wallet.Close_Encrypted_Wallet()
  956. wallet = nil
  957. windowIndex = 2.1
  958. }
  959. break
  960. // Receive DERO: Account Information
  961. case 2.4:
  962. if wallet == nil {
  963. windowIndex = 2.0
  964. break
  965. }
  966. rl.DrawTextEx(fontMenu, "Receive Payments", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  967. rl.DrawTextEx(font, "Easily receive payments using your public account information below.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  968. rl.DrawTextEx(fontSubHeader, "Public Account Address", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  969. rl.DrawTextEx(font, wallet.GetAddress().String(), rl.Vector2{offsetX, 200}, 20, spacing, rl.White)
  970. if rl.GuiButton(rl.NewRectangle(offsetX, 250, 210, 50), "Copy Address") {
  971. rl.SetClipboardText(wallet.GetAddress().String())
  972. }
  973. break
  974. // Send DERO
  975. case 2.5:
  976. if wallet.Is_View_Only() {
  977. windowIndex = 2.2
  978. break
  979. }
  980. transfer.OfflineTX = false
  981. rl.DrawTextEx(fontMenu, "Send Payments", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  982. rl.DrawTextEx(font, "/ Send / Destination Address /", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  983. rl.DrawTextEx(fontSubHeader, "Destination Address: (Ctrl-V to paste)", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  984. //_, receiverString = rl.GuiTextBox(rl.NewRectangle(offsetX, 200, 200, 50), receiverString, 198, true)
  985. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 910, 60), cmdGray)
  986. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 910, 275}, 2.0, session.Color)
  987. masked, mask := textMask(receiverString)
  988. if masked {
  989. rl.DrawTextEx(font, mask, rl.Vector2{offsetX, 231}, 20, spacing, session.Color)
  990. }
  991. if (rl.IsKeyDown(rl.KeyLeftControl) && rl.IsKeyDown(rl.KeyV)) {
  992. receiverString = rl.GetClipboardText()
  993. if len(receiverString) < 128 {
  994. masked, mask = textMask(receiverString)
  995. }
  996. }
  997. pid := false
  998. ok := false
  999. // Validate Address
  1000. transfer.rAddress, err = globals.ParseValidateAddress(receiverString)
  1001. if err != nil {
  1002. rl.DrawTextEx(font, "Invalid address format.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  1003. ok = false
  1004. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Clear") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1005. receiverString = ""
  1006. }
  1007. break
  1008. } else {
  1009. rl.DrawTextEx(font, "Valid Address", rl.Vector2{offsetX, 285}, 20, spacing, session.Color)
  1010. ok = true
  1011. }
  1012. a := *transfer.rAddress
  1013. if a.IsIntegratedAddress() {
  1014. //transfer.PaymentID, err = hex.DecodeString(pidString)
  1015. if err == nil {
  1016. pid = true
  1017. }
  1018. } else {
  1019. pid = false
  1020. }
  1021. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1022. if ok == true {
  1023. if pid {
  1024. windowIndex = 2.52
  1025. } else {
  1026. windowIndex = 2.51
  1027. }
  1028. }
  1029. }
  1030. break
  1031. case 2.51:
  1032. if wallet.Is_View_Only() {
  1033. windowIndex = 2.2
  1034. break
  1035. }
  1036. rl.DrawTextEx(fontMenu, "Send Payments", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1037. rl.DrawTextEx(font, "/ Send / Destination Address / Payment ID /", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1038. rl.DrawTextEx(fontSubHeader, "Payment ID (optional):", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1039. //_, pidString = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 200, 50), pidString, 128, true)
  1040. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 900, 60), cmdGray)
  1041. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 900, 275}, 2.0, session.Color)
  1042. masked, mask := textMask(pidString)
  1043. if masked {
  1044. rl.DrawTextEx(font, mask, rl.Vector2{offsetX, 231}, 20, spacing, session.Color)
  1045. }
  1046. if (rl.IsKeyDown(rl.KeyLeftControl) && rl.IsKeyDown(rl.KeyV)) {
  1047. pidString = rl.GetClipboardText()
  1048. masked, mask = textMask(pidString)
  1049. }
  1050. err = nil
  1051. if (rl.GuiButton(rl.NewRectangle(offsetX + 230, 350, 210, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1052. windowIndex = 2.52
  1053. }
  1054. //if pidString != "" {
  1055. if rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Clear") {
  1056. pidString = ""
  1057. transfer.PaymentID = nil
  1058. break
  1059. }
  1060. //transfer.PaymentID, err = hex.DecodeString(pidString)
  1061. if (len(pidString) == 8 || len(pidString) == 64) {
  1062. transfer.PaymentID, err = hex.DecodeString(pidString)
  1063. if err != nil {
  1064. rl.DrawTextEx(font, "Invalid payment ID format.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  1065. break
  1066. }
  1067. } else {
  1068. rl.DrawTextEx(font, "Invalid payment ID format.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  1069. break
  1070. }
  1071. //}
  1072. break
  1073. case 2.52:
  1074. if wallet.Is_View_Only() {
  1075. windowIndex = 2.2
  1076. break
  1077. }
  1078. rl.DrawTextEx(fontMenu, "Send Payments", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1079. rl.DrawTextEx(font, "/ Send / Destination Address / Payment ID / Amount /", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1080. rl.DrawTextEx(fontSubHeader, "Amount:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1081. _, amountString = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 200, 50), amountString, 60, true)
  1082. checked = rl.GuiCheckBox(rl.NewRectangle(offsetX + 520, 220, 20, 20), " All", checked)
  1083. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1084. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1085. masked, mask := textMask(amountString)
  1086. if masked {
  1087. rl.DrawTextEx(fontHeader, mask, rl.Vector2{offsetX, 231}, 30, spacing, session.Color)
  1088. }
  1089. if (rl.IsKeyDown(rl.KeyLeftControl) && rl.IsKeyDown(rl.KeyV)) {
  1090. amountString = rl.GetClipboardText()
  1091. masked, mask = textMask(amountString)
  1092. }
  1093. curBalance, _ := wallet.Get_Balance()
  1094. if checked {
  1095. amountString = globals.FormatMoney12(curBalance)
  1096. }
  1097. // Validate Amount
  1098. ok := false
  1099. transfer.Amount, err = globals.ParseAmount(amountString)
  1100. if err != nil {
  1101. ok = false
  1102. rl.DrawTextEx(font, "Invalid amount format.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  1103. break
  1104. } else {
  1105. curBalance, _ := wallet.Get_Balance()
  1106. if transfer.Amount > curBalance {
  1107. ok = false
  1108. rl.DrawTextEx(font, "Insufficient balance.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  1109. break
  1110. } else {
  1111. ok = true
  1112. }
  1113. }
  1114. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter) && ok == true) {
  1115. addr_list := []address.Address{*transfer.rAddress}
  1116. amount_list := []uint64{transfer.Amount}
  1117. fees_per_kb := uint64(0) // fees must be calculated by walletapi
  1118. if checked {
  1119. tx, inputs, input_sum, err := wallet.Transfer_Everything(*transfer.rAddress, hex.EncodeToString(transfer.PaymentID), 0, fees_per_kb, 5)
  1120. _ = inputs
  1121. if err != nil {
  1122. // TODO: Error feedback
  1123. log.Warnf("Error building the transaction: %s", err)
  1124. break
  1125. } else {
  1126. amountString = globals.FormatMoney12(input_sum)
  1127. transfer.InputSum = input_sum
  1128. transfer.Fees = tx.RctSignature.Get_TX_Fee()
  1129. transfer.Amount = curBalance - transfer.Fees
  1130. transfer.Size = float32(len(tx.Serialize()))/1024.0
  1131. transfer.OfflineTX = false
  1132. transfer.TX = tx
  1133. checked = false
  1134. windowIndex = 2.53
  1135. }
  1136. } else {
  1137. tx, inputs, input_sum, change, err := wallet.Transfer(addr_list, amount_list, 0, hex.EncodeToString(transfer.PaymentID), fees_per_kb, 0)
  1138. _ = inputs
  1139. if err != nil {
  1140. log.Warnf("Error while building transaction: %s", err)
  1141. break
  1142. }
  1143. if session.Mode == "offline" {
  1144. transfer.OfflineTX = true
  1145. } else {
  1146. transfer.OfflineTX = false
  1147. }
  1148. transfer.Relay = build_relay_transaction(tx, inputs, input_sum, change, err, transfer.OfflineTX, amount_list)
  1149. if transfer.Relay {
  1150. checked = false
  1151. windowIndex = 2.53
  1152. } else {
  1153. rl.DrawTextEx(font, "Error: Unable to build the transfer.", rl.Vector2{offsetX, 515}, 20, spacing, rl.Magenta)
  1154. break
  1155. }
  1156. }
  1157. }
  1158. break
  1159. case 2.53:
  1160. if wallet.Is_View_Only() {
  1161. windowIndex = 2.2
  1162. break
  1163. }
  1164. if passwordError {
  1165. rl.DrawTextEx(font, "The password entered is incorrect.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  1166. }
  1167. rl.DrawTextEx(fontMenu, "Send Payments", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1168. rl.DrawTextEx(font, "/ Send / Destination Address / Payment ID / Amount / Confirmation /", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1169. rl.DrawTextEx(fontSubHeader, "My Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1170. strAmount := globals.FormatMoney12(transfer.Amount)
  1171. strFees := globals.FormatMoney12(transfer.Fees)
  1172. rl.DrawTextEx(font, "TRANSACTION DETAILS\n\nDestination Address: " + transfer.rAddress.String() + "\nPayment ID: " + hex.EncodeToString(transfer.PaymentID) + "\nAmount: " + strAmount + "\nFees: " + strFees, rl.Vector2{offsetX, 350}, 20, spacing, rl.White)
  1173. _, loginPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), loginPassword, 160, true)
  1174. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1175. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1176. masked, mask := passwordMask(loginPassword)
  1177. if masked {
  1178. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1179. }
  1180. if (rl.GuiButton(rl.NewRectangle(offsetX, 600, 210, 50), "Confirm") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1181. check := wallet.Check_Password(loginPassword)
  1182. if check == false {
  1183. loginPassword = ""
  1184. passwordError = true
  1185. break
  1186. } else {
  1187. if transfer.OfflineTX { // if its an offline tx, dump it to a file
  1188. cur_dir, err := os.Getwd()
  1189. if err != nil {
  1190. break
  1191. }
  1192. transfer.TXID = transfer.TX.GetHash()
  1193. filename := filepath.Join(cur_dir, transfer.TXID.String() + ".tx")
  1194. err = ioutil.WriteFile(filename, []byte(hex.EncodeToString(transfer.TX.Serialize())), 0600)
  1195. if err == nil {
  1196. transfer.Filename = filename
  1197. transfer.Status = "Success"
  1198. } else {
  1199. transfer.Status = "Failed"
  1200. log.Warnf("Error building offline transaction: %s", err)
  1201. }
  1202. } else {
  1203. loginPassword = ""
  1204. passwordError = false
  1205. err = wallet.SendTransaction(transfer.TX) // relay tx to daemon/network
  1206. if err == nil {
  1207. transfer.Status = "Success"
  1208. transfer.TXID = transfer.TX.GetHash()
  1209. } else {
  1210. transfer.Status = "Failed"
  1211. transfer.TXID = transfer.TX.GetHash()
  1212. log.Warnf("Error relaying transaction: %s", err)
  1213. }
  1214. }
  1215. windowIndex = 2.54
  1216. }
  1217. }
  1218. if rl.GuiButton(rl.NewRectangle(offsetX + 230, 600, 210, 50), "Cancel") {
  1219. resetTransfer()
  1220. windowIndex = 2.2
  1221. }
  1222. break
  1223. case 2.54:
  1224. if wallet.Is_View_Only() {
  1225. windowIndex = 2.2
  1226. break
  1227. }
  1228. transferText := ""
  1229. transferHeader := ""
  1230. transferMessage := ""
  1231. if transfer.Status == "Success" {
  1232. if transfer.OfflineTX {
  1233. transferText = "Your offline payment was saved."
  1234. transferHeader = "Transaction Path:"
  1235. transferMessage = APP_PATH + transfer.Filename
  1236. } else {
  1237. transferText = "Your payment has been sent successfully!"
  1238. transferHeader = "Transaction ID:"
  1239. transferMessage = transfer.TXID.String()
  1240. }
  1241. } else {
  1242. transferText = "Your payment has been declined."
  1243. }
  1244. rl.DrawTextEx(fontMenu, "Send Payments", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1245. rl.DrawTextEx(font, "Payment Status", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1246. rl.DrawTextEx(fontHeader, transferText, rl.Vector2{offsetX, 150}, 30, spacing, session.Color)
  1247. rl.DrawTextEx(fontSubHeader, transferHeader, rl.Vector2{offsetX, 200}, 25, spacing, rl.White)
  1248. rl.DrawTextEx(font, transferMessage, rl.Vector2{offsetX, 250}, 20, spacing, rl.White)
  1249. if rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Return") {
  1250. resetTransfer()
  1251. windowIndex = 2.2
  1252. }
  1253. if transfer.OfflineTX == false {
  1254. if rl.GuiButton(rl.NewRectangle(offsetX + 230, 350, 250, 50), "View Transaction") {
  1255. openURL("http://explorer.dero.io/tx/" + transfer.TXID.String())
  1256. }
  1257. }
  1258. break
  1259. // View History
  1260. case 2.6:
  1261. rl.DrawTextEx(fontMenu, "View History", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1262. rl.DrawTextEx(font, "Access all information related to your transactions.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1263. //rl.DrawTextEx(fontHeader, "My Transactions", rl.Vector2{offsetX, 150}, 30, spacing, session.Color)
  1264. record := make([]Record, 3)
  1265. available := true
  1266. in := true
  1267. out := true
  1268. pool := true
  1269. failed := false
  1270. min_height := uint64(0)
  1271. max_height := uint64(0)
  1272. transfers := wallet.Show_Transfers(available, in, out, pool, failed, false, min_height, max_height)
  1273. if len(transfers) == 0 {
  1274. rl.DrawTextEx(font, "No transaction history.", rl.Vector2{offsetX, 150}, 20, spacing, rl.Gray)
  1275. break
  1276. } else {
  1277. increment := 3
  1278. posY := float32(150)
  1279. if len(transfers) < 3 {
  1280. increment = len(transfers)
  1281. }
  1282. for i := 0; i < increment; i++ {
  1283. if i == 0 {
  1284. posY = 150
  1285. } else if i == 1 {
  1286. posY = 350
  1287. } else {
  1288. posY = 550
  1289. }
  1290. if i > len(transfers) {
  1291. break
  1292. } else if i < 0 {
  1293. break
  1294. } else if (start + i) >= len(transfers) {
  1295. break
  1296. }
  1297. record[i].txNumber = uint64(start + i)
  1298. record[i].txTime = string(transfers[record[i].txNumber].Time.Format(time.RFC822))
  1299. record[i].txHeight = strconv.FormatUint(transfers[record[i].txNumber].Height, 10)
  1300. record[i].txTopoHeight = strconv.FormatInt(transfers[record[i].txNumber].TopoHeight, 10)
  1301. record[i].txID = transfers[record[i].txNumber].TXID.String()
  1302. record[i].txPaymentID = string(transfers[record[i].txNumber].PaymentID)
  1303. record[i].txAmount = globals.FormatMoney12(transfers[record[i].txNumber].Amount)
  1304. record[i].txKey = string(wallet.GetTXKey(crypto.HexToHash(record[i].txID)))
  1305. record[i].txColor = rl.Gray
  1306. if record[i].txKey != "" && record[i].txKey != "None" {
  1307. if (rl.GuiLabelButton(rl.NewRectangle(offsetX + 700, posY + 50, 200, 50), "Copy Transaction Key")) {
  1308. rl.SetClipboardText(record[i].txKey)
  1309. }
  1310. } else {
  1311. record[i].txKey = "None"
  1312. }
  1313. switch transfers[record[i].txNumber].Status {
  1314. case 0:
  1315. record[i].txColor = session.Color
  1316. rl.DrawTextEx(font, record[i].txTime + "\nTransaction ID: " + record[i].txID + "\nRECEIVED " + record[i].txAmount + "\n[ " + record[i].txHeight + " / " + record[i].txTopoHeight + " ]\nTransaction Key: " + record[i].txKey, rl.Vector2{offsetX + 30, posY}, 20, spacing, rl.White)
  1317. break
  1318. case 1:
  1319. record[i].txColor = rl.Magenta
  1320. rl.DrawTextEx(font, record[i].txTime + "\nTransaction ID: " + record[i].txID + "\nSPENT " + record[i].txAmount + "\n[ " + record[i].txHeight + " / " + record[i].txTopoHeight + " ]\nTransaction Key: " + record[i].txKey, rl.Vector2{offsetX + 30, posY}, 20, spacing, rl.White)
  1321. break
  1322. case 2:
  1323. fallthrough
  1324. default:
  1325. record[i].txColor = rl.Gray
  1326. rl.DrawTextEx(font, record[i].txTime + "\nTransaction ID: " + record[i].txID + "\nTransaction status unknown\n" + string(transfers[i].Status), rl.Vector2{offsetX + 30, posY}, 20, spacing, rl.White)
  1327. }
  1328. rl.DrawLineEx(rl.Vector2{offsetX + 5, posY}, rl.Vector2{offsetX + 5, posY + 145}, 4.0, record[i].txColor)
  1329. rl.DrawLineEx(rl.Vector2{offsetX + 680, posY}, rl.Vector2{offsetX + 680, posY + 145}, 1.0, rl.DarkGray)
  1330. if (rl.GuiLabelButton(rl.NewRectangle(offsetX + 700, posY, 200, 50), "View in Explorer")) {
  1331. openURL("http://explorer.dero.io/tx/" + record[i].txID)
  1332. }
  1333. if (rl.GuiLabelButton(rl.NewRectangle(float32(rl.GetScreenWidth() - 300), 30, 100, 50), "< Previous")) {
  1334. if start >= 3 {
  1335. start -= 3
  1336. }
  1337. }
  1338. if (rl.GuiLabelButton(rl.NewRectangle(float32(rl.GetScreenWidth() - 130), 30, 100, 50), "Next >")) {
  1339. if start < len(transfers) - 2 {
  1340. start += 3
  1341. }
  1342. }
  1343. }
  1344. }
  1345. break
  1346. // Options
  1347. case 2.7:
  1348. if passwordError == true {
  1349. rl.DrawTextEx(font, "The password entered was incorrect.", rl.Vector2{offsetX, 285}, 20, 0, rl.Gray)
  1350. }
  1351. rl.DrawTextEx(fontMenu, "Account Options", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1352. rl.DrawTextEx(font, "View and update your account information.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1353. rl.DrawTextEx(fontSubHeader, "My Password", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1354. _, loginPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), loginPassword, 160, true)
  1355. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1356. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1357. masked, mask := passwordMask(loginPassword)
  1358. if masked {
  1359. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1360. }
  1361. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Confirm") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1362. check := wallet.Check_Password(loginPassword)
  1363. if check == false {
  1364. loginPassword = ""
  1365. passwordError = true
  1366. break
  1367. } else {
  1368. passwordError = false
  1369. loginPassword = ""
  1370. windowIndex = 2.71
  1371. }
  1372. }
  1373. break
  1374. // Options Menu
  1375. case 2.71:
  1376. clearVars()
  1377. rl.DrawTextEx(fontMenu, "Account Options", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1378. rl.DrawTextEx(font, "View and update your account information.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1379. // Seed
  1380. rl.DrawTextEx(fontSubHeader, "Your 25 recovery words (seed) can be used to restore your account.", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1381. if rl.GuiButton(rl.NewRectangle(offsetX, 210, 210, 50), "Copy Seed") {
  1382. rl.SetClipboardText(wallet.GetSeed())
  1383. }
  1384. // View wallet key
  1385. rl.DrawTextEx(fontSubHeader, "A view key can be used to restore a view-only account.", rl.Vector2{offsetX, 320}, 25, spacing, rl.White)
  1386. if rl.GuiButton(rl.NewRectangle(offsetX, 380, 210, 50), "Copy Key") {
  1387. rl.SetClipboardText(wallet.GetViewWalletKey())
  1388. }
  1389. // Change Password
  1390. rl.DrawTextEx(fontSubHeader, "Update your account password.", rl.Vector2{offsetX, 490}, 25, spacing, rl.White)
  1391. if rl.GuiButton(rl.NewRectangle(offsetX, 550, 210, 50), "Update") {
  1392. windowIndex = 2.72
  1393. }
  1394. break
  1395. // Change Password
  1396. case 2.72:
  1397. if passwordError == true {
  1398. rl.DrawTextEx(font, "The passwords entered do not match.", rl.Vector2{offsetX, 285}, 20, 0, rl.Gray)
  1399. }
  1400. rl.DrawTextEx(fontMenu, "Account Options", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1401. rl.DrawTextEx(font, "View and update your account information.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1402. rl.DrawTextEx(fontSubHeader, "New Password", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1403. _, loginPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), loginPassword, 160, true)
  1404. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1405. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1406. masked, mask := passwordMask(loginPassword)
  1407. if masked {
  1408. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1409. }
  1410. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1411. windowIndex = 2.73
  1412. }
  1413. break
  1414. // Change Password cont.
  1415. case 2.73:
  1416. rl.DrawTextEx(fontMenu, "Account Options", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1417. rl.DrawTextEx(font, "View and update your account information.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1418. rl.DrawTextEx(fontSubHeader, "Confirm Password", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1419. _, tempPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), tempPassword, 160, true)
  1420. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1421. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1422. masked, mask := passwordMask(tempPassword)
  1423. if masked {
  1424. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1425. }
  1426. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 210, 50), "Confirm") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1427. if loginPassword == tempPassword {
  1428. err = wallet.Set_Encrypted_Wallet_Password(loginPassword)
  1429. if err != nil {
  1430. // TODO: Error feedback
  1431. log.Warnf("Error changing password: %s", err)
  1432. break
  1433. } else {
  1434. passwordError = false
  1435. loginPassword = ""
  1436. tempPassword = ""
  1437. windowIndex = 2.74
  1438. }
  1439. } else {
  1440. loginPassword = ""
  1441. tempPassword = ""
  1442. passwordError = true
  1443. windowIndex = 2.72
  1444. }
  1445. }
  1446. break
  1447. // Change Password cont.
  1448. case 2.74:
  1449. rl.DrawTextEx(fontMenu, "Account Options", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1450. rl.DrawTextEx(font, "View and update your account information.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1451. rl.DrawTextEx(fontSubHeader, "Your password has been updated successfully.", rl.Vector2{offsetX, 150}, 25, spacing, session.Color)
  1452. if rl.GuiButton(rl.NewRectangle(offsetX, 250, 210, 50), "Return") {
  1453. windowIndex = 2.71
  1454. }
  1455. break
  1456. // Restore an account
  1457. case 3.0:
  1458. clearVars()
  1459. if wallet != nil {
  1460. windowIndex = 2.2
  1461. }
  1462. restorePassword = ""
  1463. tempPassword = ""
  1464. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1465. rl.DrawTextEx(font, "Choose one of the methods below to restore your account.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1466. if rl.GuiLabelButton(rl.NewRectangle(offsetX, 150, 250, 50), "_ View-Only using Hex (128 chars)") {
  1467. windowIndex = 3.1
  1468. }
  1469. if rl.GuiLabelButton(rl.NewRectangle(offsetX, 250, 250, 50), "_ Restore using Seed (25 words)") {
  1470. windowIndex = 3.2
  1471. }
  1472. if rl.GuiLabelButton(rl.NewRectangle(offsetX, 350, 250, 50), "_ Restore using Hex (64 chars)") {
  1473. windowIndex = 3.3
  1474. }
  1475. break
  1476. // Restore View-Only using Hex (128 chars)
  1477. case 3.1:
  1478. if fileError == true {
  1479. rl.DrawTextEx(font, "That account name already exists.", rl.Vector2{offsetX, 285}, 20, 0, rl.Gray)
  1480. }
  1481. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1482. rl.DrawTextEx(font, "Restore View-Only with Hex (128 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1483. rl.DrawTextEx(fontSubHeader, "Account Name:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1484. _, restoreFilename = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), restoreFilename, 60, restoreFilenameEditable)
  1485. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1486. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1487. restoreFilename = strings.Trim(restoreFilename, ".")
  1488. restoreFilename = strings.Trim(restoreFilename, " ")
  1489. masked, mask := textMask(restoreFilename)
  1490. if masked {
  1491. rl.DrawTextEx(fontHeader, mask, rl.Vector2{offsetX, 231}, 30, spacing, session.Color)
  1492. }
  1493. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1494. if restoreFilename != "" {
  1495. if _, err = os.Stat(APP_PATH + restoreFilename + ".db"); err != nil {
  1496. fileError = false
  1497. windowIndex = 3.12
  1498. } else {
  1499. fileError = true
  1500. }
  1501. }
  1502. }
  1503. break
  1504. case 3.12:
  1505. if passwordError == true {
  1506. rl.DrawTextEx(font, "The passwords entered do not match.", rl.Vector2{offsetX, 285}, 20, 0, rl.Gray)
  1507. }
  1508. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1509. rl.DrawTextEx(font, "Restore View-Only with Hex (128 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1510. rl.DrawTextEx(fontSubHeader, "Account Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1511. _, restorePassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), restorePassword, 60, restorePasswordEditable)
  1512. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1513. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1514. masked, mask := passwordMask(restorePassword)
  1515. if masked {
  1516. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1517. }
  1518. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1519. if restorePassword != "" {
  1520. windowIndex = 3.121
  1521. }
  1522. }
  1523. break
  1524. case 3.121:
  1525. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1526. rl.DrawTextEx(font, "Restore View-Only with Hex (128 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1527. rl.DrawTextEx(fontSubHeader, "Confirm Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1528. tempPasswordUpdated, tempPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), tempPassword, 60, tempPasswordEditable)
  1529. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1530. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1531. masked, mask := passwordMask(tempPassword)
  1532. if masked {
  1533. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1534. }
  1535. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1536. if restorePassword != "" && restorePassword != tempPassword {
  1537. passwordError = true
  1538. restorePassword = ""
  1539. tempPassword = ""
  1540. windowIndex = 3.12
  1541. } else {
  1542. passwordError = false
  1543. windowIndex = 3.13
  1544. }
  1545. }
  1546. break
  1547. case 3.13:
  1548. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1549. rl.DrawTextEx(font, "Restore View-Only with Hex (128 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1550. rl.DrawTextEx(fontSubHeader, "Enter your 128 character hex (Ctrl-V to Paste) :", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1551. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1552. rl.DrawLineEx(rl.Vector2{offsetX, 300}, rl.Vector2{offsetX + 800, 300}, 2.0, session.Color)
  1553. if (rl.IsKeyDown(rl.KeyLeftControl) && rl.IsKeyDown(rl.KeyV)) {
  1554. restoreHex = string(rl.GetClipboardText())
  1555. }
  1556. rl.DrawTextRecEx(font, restoreHex, rl.NewRectangle(offsetX, 231, 800, 200), 20, 0, true, session.Color, 0, 0, rl.Gray, statusColor)
  1557. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1558. if (restoreHex != "" && len(restoreHex) == 128) {
  1559. windowIndex = 3.14
  1560. }
  1561. }
  1562. break
  1563. case 3.14:
  1564. if (restoreFilename != "" && restorePassword != "" && restoreHex != "") {
  1565. wallet, err = walletapi.Create_Encrypted_Wallet_ViewOnly(restoreFilename + ".db", restorePassword, restoreHex)
  1566. if err != nil {
  1567. log.Warnf("Error while reconstructing view-only wallet using view key: %s", err)
  1568. } else {
  1569. err = wallet.Set_Encrypted_Wallet_Password(restorePassword)
  1570. if err != nil {
  1571. log.Warnf("Error changing password: %s", err)
  1572. } else {
  1573. closeWallet()
  1574. windowIndex = 2.1
  1575. session.Path = restoreFilename + ".db"
  1576. restoreFilename = ""
  1577. restorePassword = ""
  1578. tempPassword = ""
  1579. restoreHex = ""
  1580. fileError = false
  1581. passwordError = false
  1582. }
  1583. }
  1584. }
  1585. break
  1586. // Restore using Seed (25 words)
  1587. case 3.2:
  1588. if fileError == true {
  1589. rl.DrawTextEx(font, "That account name already exists.", rl.Vector2{offsetX, 285}, 20, 0, rl.Gray)
  1590. }
  1591. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1592. rl.DrawTextEx(font, "Restore using Seed (25 words)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1593. rl.DrawTextEx(fontSubHeader, "Account Name:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1594. _, restoreFilename = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), restoreFilename, 60, restoreFilenameEditable)
  1595. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1596. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1597. restoreFilename = strings.Trim(restoreFilename, ".")
  1598. restoreFilename = strings.Trim(restoreFilename, " ")
  1599. masked, mask := textMask(restoreFilename)
  1600. if masked {
  1601. rl.DrawTextEx(fontHeader, mask, rl.Vector2{offsetX, 231}, 30, spacing, session.Color)
  1602. }
  1603. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1604. if restoreFilename != "" {
  1605. if _, err = os.Stat(APP_PATH + restoreFilename + ".db"); err != nil {
  1606. fileError = false
  1607. windowIndex = 3.21
  1608. } else {
  1609. fileError = true
  1610. }
  1611. } else {
  1612. // error handling
  1613. }
  1614. }
  1615. break
  1616. case 3.21:
  1617. if passwordError == true {
  1618. rl.DrawTextEx(font, "Passwords do not match.", rl.Vector2{offsetX, 285}, 20, spacing, rl.Gray)
  1619. }
  1620. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1621. rl.DrawTextEx(font, "Restore using Seed (25 words)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1622. rl.DrawTextEx(fontSubHeader, "Account Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1623. _, restorePassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), restorePassword, 160, restorePasswordEditable)
  1624. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1625. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1626. masked, mask := passwordMask(restorePassword)
  1627. if masked {
  1628. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1629. }
  1630. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1631. if restorePassword != "" {
  1632. windowIndex = 3.22
  1633. } else {
  1634. // error handling
  1635. }
  1636. }
  1637. break
  1638. case 3.22:
  1639. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1640. rl.DrawTextEx(font, "Restore using Seed (25 words)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1641. rl.DrawTextEx(fontSubHeader, "Confirm Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1642. tempPasswordUpdated, tempPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), tempPassword, 160, tempPasswordEditable)
  1643. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1644. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1645. masked, mask := passwordMask(tempPassword)
  1646. if masked {
  1647. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1648. }
  1649. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1650. if restorePassword != "" && restorePassword != tempPassword {
  1651. passwordError = true
  1652. restorePassword = ""
  1653. tempPassword = ""
  1654. windowIndex = 3.21
  1655. } else {
  1656. passwordError = false
  1657. windowIndex = 3.23
  1658. }
  1659. }
  1660. break
  1661. case 3.23:
  1662. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1663. rl.DrawTextEx(font, "Restore using Seed (25 words)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1664. rl.DrawTextEx(fontSubHeader, "Enter your 25 recovery words (Ctrl-V to Paste) :", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1665. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1666. rl.DrawLineEx(rl.Vector2{offsetX, 300}, rl.Vector2{offsetX + 800, 300}, 2.0, session.Color)
  1667. if (rl.IsKeyDown(rl.KeyLeftControl) && rl.IsKeyDown(rl.KeyV)) {
  1668. restoreHex = string(rl.GetClipboardText())
  1669. }
  1670. rl.DrawTextRecEx(font, restoreHex, rl.NewRectangle(offsetX, 231, 800, 200), 20, 0, true, session.Color, 0, 0, rl.Gray, statusColor)
  1671. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1672. if restoreHex != "" {
  1673. windowIndex = 3.24
  1674. } else {
  1675. // error handling
  1676. }
  1677. }
  1678. break
  1679. case 3.24:
  1680. if (restoreFilename != "" && restorePassword != "" && restoreHex != "") {
  1681. wallet, err = walletapi.Create_Encrypted_Wallet_From_Recovery_Words(restoreFilename + ".db", restorePassword, restoreHex)
  1682. if err != nil {
  1683. log.Warnf("Error while reconstructing view-only wallet using view key: %s", err)
  1684. } else {
  1685. err = wallet.Set_Encrypted_Wallet_Password(restorePassword)
  1686. if err != nil {
  1687. log.Warnf("Error changing password: %s", err)
  1688. } else {
  1689. closeWallet()
  1690. session.Path = restoreFilename + ".db"
  1691. windowIndex = 2.1
  1692. restoreFilename = ""
  1693. restorePassword = ""
  1694. tempPassword = ""
  1695. restoreHex = ""
  1696. fileError = false
  1697. passwordError = false
  1698. }
  1699. }
  1700. }
  1701. break
  1702. // Restore using Hex (64 chars)
  1703. case 3.3:
  1704. if fileError == true {
  1705. rl.DrawTextEx(font, "That account name already exists.", rl.Vector2{offsetX, 285}, 20, 0, rl.Gray)
  1706. }
  1707. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1708. rl.DrawTextEx(font, "Restore using Hex (64 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1709. rl.DrawTextEx(fontSubHeader, "Account Name:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1710. _, restoreFilename = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), restoreFilename, 60, restoreFilenameEditable)
  1711. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1712. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1713. masked, mask := textMask(restoreFilename)
  1714. if masked {
  1715. rl.DrawTextEx(fontHeader, mask, rl.Vector2{offsetX, 231}, 30, spacing, session.Color)
  1716. }
  1717. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1718. if restoreFilename != "" {
  1719. if _, err = os.Stat(APP_PATH + restoreFilename + ".db"); err != nil {
  1720. fileError = false
  1721. windowIndex = 3.21
  1722. } else {
  1723. fileError = true
  1724. }
  1725. } else {
  1726. // error handling
  1727. }
  1728. }
  1729. break
  1730. case 3.31:
  1731. if passwordError == true {
  1732. rl.DrawTextEx(font, "Passwords do not match.", rl.Vector2{400, 285}, 20, spacing, rl.Gray)
  1733. }
  1734. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1735. rl.DrawTextEx(font, "Restore using Hex (64 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1736. rl.DrawTextEx(fontSubHeader, "Account Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1737. _, restorePassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), restorePassword, 160, restorePasswordEditable)
  1738. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1739. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1740. masked, mask := passwordMask(restorePassword)
  1741. if masked {
  1742. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1743. }
  1744. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1745. if restorePassword != "" {
  1746. windowIndex = 3.22
  1747. } else {
  1748. // error handling
  1749. }
  1750. }
  1751. break
  1752. case 3.32:
  1753. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1754. rl.DrawTextEx(font, "Restore using Hex (64 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1755. rl.DrawTextEx(fontSubHeader, "Confirm Password:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1756. _, tempPassword = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 400, 50), tempPassword, 160, tempPasswordEditable)
  1757. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 500, 60), cmdGray)
  1758. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1759. masked, mask := passwordMask(tempPassword)
  1760. if masked {
  1761. rl.DrawTextEx(fontPassword, mask, rl.Vector2{offsetX, 231}, 50, spacing, session.Color)
  1762. }
  1763. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1764. if restorePassword != "" && restorePassword != tempPassword {
  1765. passwordError = true
  1766. windowIndex = 3.21
  1767. } else {
  1768. passwordError = false
  1769. windowIndex = 3.23
  1770. }
  1771. }
  1772. break
  1773. case 3.33:
  1774. rl.DrawTextEx(fontMenu, "Restore an Account", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1775. rl.DrawTextEx(font, "Restore using Hex (64 chars)", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1776. rl.DrawTextEx(fontSubHeader, "Enter your 65 hex chars (Ctrl-V to Paste) :", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1777. //restoreHexUpdated, restoreHex = rl.GuiTextBoxMulti(rl.NewRectangle(offsetX, 300, 1000, 200), restoreHex, 64, restoreHexEditable)
  1778. if (rl.IsKeyDown(rl.KeyLeftControl) && rl.IsKeyDown(rl.KeyV)) {
  1779. restoreHex = string(rl.GetClipboardText())
  1780. }
  1781. rl.DrawTextRecEx(font, restoreHex, rl.NewRectangle(offsetX, 231, 800, 200), 20, 0, true, session.Color, 0, 0, rl.Gray, statusColor)
  1782. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Next") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1783. if restoreHex != "" {
  1784. windowIndex = 3.24
  1785. } else {
  1786. // error handling
  1787. }
  1788. }
  1789. break
  1790. case 3.34:
  1791. if (restoreFilename != "" && restorePassword != "" && restoreHex != "") {
  1792. wallet, err = walletapi.Create_Encrypted_Wallet_From_Recovery_Words(restoreFilename + ".db", restorePassword, restoreHex)
  1793. if err != nil {
  1794. log.Warnf("Error while reconstructing view-only wallet using view key: %s", err)
  1795. } else {
  1796. err = wallet.Set_Encrypted_Wallet_Password(restorePassword)
  1797. if err != nil {
  1798. log.Warnf("Error changing password: %s", err)
  1799. } else {
  1800. closeWallet()
  1801. session.Path = restoreFilename + ".db"
  1802. windowIndex = 2.1
  1803. restoreFilename = ""
  1804. restorePassword = ""
  1805. tempPassword = ""
  1806. restoreHex = ""
  1807. fileError = false
  1808. passwordError = false
  1809. }
  1810. }
  1811. }
  1812. break
  1813. case 4.0:
  1814. clearVars()
  1815. rl.DrawTextEx(fontMenu, "Settings", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1816. rl.DrawTextEx(font, "View and update application-specific settings.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1817. //rl.DrawTextEx(fontSubHeader, "Settings have been disabled in this release.", rl.Vector2{offsetX, 150}, 25, spacing, rl.Orange)
  1818. break
  1819. // Network
  1820. case 4.1:
  1821. clearVars()
  1822. rl.DrawTextEx(fontMenu, "Network", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1823. rl.DrawTextEx(font, "Choose your default network.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1824. active := 0
  1825. if session.Network == "Mainnet" {
  1826. active = 0
  1827. } else {
  1828. active = 1
  1829. }
  1830. networks := "Mainnet;Testnet"
  1831. networkList := strings.Split(networks, ";")
  1832. combo := rl.GuiComboBox(rl.NewRectangle(offsetX, 150, 600, 50), networks, active)
  1833. if combo != active {
  1834. active = combo
  1835. }
  1836. session.Network = networkList[active]
  1837. config.DefaultNetwork = networkList[active]
  1838. data := Config {
  1839. RemoteDaemon: config.RemoteDaemon,
  1840. RemoteDaemonTestnet: config.RemoteDaemonTestnet,
  1841. LocalDaemon: config.LocalDaemon,
  1842. LocalDaemonTestnet: config.LocalDaemonTestnet,
  1843. RPCAuth: config.RPCAuth,
  1844. RPCAddress: config.RPCAddress,
  1845. DefaultMode: config.DefaultMode,
  1846. DefaultNetwork: networkList[active],
  1847. }
  1848. file, _ := json.MarshalIndent(data, "", " ")
  1849. _ = ioutil.WriteFile("config.json", file, 0644)
  1850. break
  1851. // Network Mode
  1852. case 4.2:
  1853. clearVars()
  1854. rl.DrawTextEx(fontMenu, "Network Mode", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1855. rl.DrawTextEx(font, "Choose your default network mode.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1856. active := 0
  1857. if session.Mode == "remote" {
  1858. active = 0
  1859. } else if session.Mode == "local" {
  1860. active = 1
  1861. } else {
  1862. active = 2
  1863. }
  1864. modes := "remote;local;offline"
  1865. modeList := strings.Split(modes, ";")
  1866. combo := rl.GuiComboBox(rl.NewRectangle(offsetX, 150, 600, 50), modes, active)
  1867. if combo != active {
  1868. active = combo
  1869. }
  1870. session.Mode = modeList[active]
  1871. config.DefaultMode = modeList[active]
  1872. data := Config {
  1873. RemoteDaemon: config.RemoteDaemon,
  1874. RemoteDaemonTestnet: config.RemoteDaemonTestnet,
  1875. LocalDaemon: config.LocalDaemon,
  1876. LocalDaemonTestnet: config.LocalDaemonTestnet,
  1877. RPCAuth: config.RPCAuth,
  1878. RPCAddress: config.RPCAddress,
  1879. DefaultMode: modeList[active],
  1880. DefaultNetwork: config.DefaultNetwork,
  1881. }
  1882. file, _ := json.MarshalIndent(data, "", " ")
  1883. _ = ioutil.WriteFile("config.json", file, 0644)
  1884. break
  1885. // Daemon
  1886. case 4.3:
  1887. clearVars()
  1888. em = true
  1889. if config.RemoteDaemon != DEFAULT_REMOTE_NODE {
  1890. checked = false
  1891. }
  1892. rl.DrawTextEx(fontMenu, "Daemon", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1893. rl.DrawTextEx(font, "Configure remote daemon addresses.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1894. rl.DrawTextEx(fontSubHeader, "Remote Node:", rl.Vector2{offsetX, 150}, 25, spacing, rl.White)
  1895. _, config.RemoteDaemon = rl.GuiTextBox(rl.NewRectangle(offsetX, 220, 900, 50), config.RemoteDaemon, 260, em)
  1896. rl.DrawRectangleRec(rl.NewRectangle(offsetX, 215, 950, 60), cmdGray)
  1897. rl.DrawLineEx(rl.Vector2{offsetX, 275}, rl.Vector2{offsetX + 500, 275}, 2.0, session.Color)
  1898. checked = rl.GuiCheckBox(rl.NewRectangle(offsetX + 520, 220, 20, 20), " Default", checked)
  1899. if em {
  1900. masked, mask := textMask(config.RemoteDaemon)
  1901. if masked {
  1902. rl.DrawTextEx(font, mask, rl.Vector2{offsetX, 231}, 20, spacing, session.Color)
  1903. }
  1904. if (rl.GuiButton(rl.NewRectangle(offsetX, 350, 150, 50), "Save") || rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter)) {
  1905. }
  1906. }
  1907. if checked {
  1908. config.RemoteDaemon = DEFAULT_REMOTE_NODE
  1909. }
  1910. data := Config {
  1911. RemoteDaemon: config.RemoteDaemon,
  1912. RemoteDaemonTestnet: config.RemoteDaemonTestnet,
  1913. LocalDaemon: config.LocalDaemon,
  1914. LocalDaemonTestnet: config.LocalDaemonTestnet,
  1915. RPCAuth: config.RPCAuth,
  1916. RPCAddress: config.RPCAddress,
  1917. DefaultMode: config.DefaultMode,
  1918. DefaultNetwork: config.DefaultNetwork,
  1919. }
  1920. file, _ := json.MarshalIndent(data, "", " ")
  1921. _ = ioutil.WriteFile("config.json", file, 0644)
  1922. break
  1923. // RPC Server
  1924. case 4.4:
  1925. clearVars()
  1926. rl.DrawTextEx(fontMenu, "RPC Server", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1927. rl.DrawTextEx(font, "Configure authentication and address for your RPC server.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1928. data := Config {
  1929. RemoteDaemon: config.RemoteDaemon,
  1930. RemoteDaemonTestnet: config.RemoteDaemonTestnet,
  1931. LocalDaemon: config.LocalDaemon,
  1932. LocalDaemonTestnet: config.LocalDaemonTestnet,
  1933. RPCAuth: config.RPCAuth,
  1934. RPCAddress: config.RPCAddress,
  1935. DefaultMode: config.DefaultMode,
  1936. DefaultNetwork: config.DefaultNetwork,
  1937. }
  1938. file, _ := json.MarshalIndent(data, "", " ")
  1939. _ = ioutil.WriteFile("config.json", file, 0644)
  1940. break
  1941. case 4.5:
  1942. clearVars()
  1943. rl.DrawTextEx(fontMenu, "Version", rl.Vector2{offsetX, 30}, 40, spacing, rl.Gray)
  1944. rl.DrawTextEx(font, "Application version information.", rl.Vector2{offsetX, 80}, 20, spacing, rl.Gray)
  1945. rl.DrawTextEx(font, "CMD v" + Version.String() + "\n\nCopyright 2020 DERO Foundation. All rights reserved.\nPrivacy Together.", rl.Vector2{offsetX, 150}, 20, spacing, session.Color)
  1946. break
  1947. case 5.0:
  1948. rl.DrawRectangleRec(rl.NewRectangle(0, 0, float32(rl.GetScreenWidth()), float32(rl.GetScreenHeight())), terminalBG)
  1949. rl.DrawTextRec(font, "CMD Terminal", rl.NewRectangle(10, 10, float32(rl.GetScreenWidth() - 20), float32(rl.GetScreenHeight() - 40)), 20, spacing, true, session.Color)
  1950. em, command = rl.GuiTextBox(rl.NewRectangle(0, float32(rl.GetScreenHeight() - 30), float32(rl.GetScreenWidth()), 30), command, 256, true)
  1951. if rl.IsKeyPressed(rl.KeyEnter) || rl.IsKeyPressed(rl.KeyKpEnter) {
  1952. if command == "exit" {
  1953. toggleTerminal(fontHeader, font)
  1954. }
  1955. }
  1956. break
  1957. default:
  1958. windowIndex = 0
  1959. }
  1960. }
  1961. rl.EndDrawing()
  1962. }
  1963. if wallet != nil {
  1964. closeWallet()
  1965. }
  1966. rl.CloseWindow()
  1967. os.Exit(0)
  1968. }
  1969. func closeWallet() {
  1970. session.RPCServer = false
  1971. wallet.Stop_RPC_Server()
  1972. wallet.SetOfflineMode()
  1973. session.Syncing = false
  1974. wallet.Close_Encrypted_Wallet()
  1975. session.Path = ""
  1976. wallet = nil
  1977. statusColor = rl.White
  1978. statusText = "Ready"
  1979. windowIndex = 2.0
  1980. progressNow = 0
  1981. start = 0
  1982. }
  1983. func syncStatus() {
  1984. if wallet != nil {
  1985. wHeight = uint64(wallet.Get_TopoHeight())
  1986. dHeight = uint64(wallet.Daemon_TopoHeight)
  1987. if dHeight == 0 {
  1988. statusColor = rl.Red
  1989. statusText = "Status: [ " + strconv.FormatUint(wHeight, 10) + " / " + strconv.FormatUint(dHeight, 10) + " ] Attempting connection to " + session.Mode + " node: " + session.Daemon
  1990. } else if (dHeight - wHeight <= 5) {
  1991. statusColor = session.Color
  1992. statusText = "Status: [ " + strconv.FormatUint(wHeight, 10) + " / " + strconv.FormatUint(dHeight, 10) + " ] Connected to " + session.Mode + " node: " + wallet.Daemon_Endpoint
  1993. progressNow = (float32(wHeight)/float32(dHeight)) * float32(rl.GetScreenWidth())
  1994. } else {
  1995. statusColor = rl.White
  1996. statusText = "Status: [ " + strconv.FormatUint(wHeight, 10) + " / " + strconv.FormatUint(dHeight, 10) + " ] Connected to " + session.Mode + " node: " + wallet.Daemon_Endpoint
  1997. progressNow = (float32(wHeight)/float32(dHeight)) * float32(rl.GetScreenWidth())
  1998. }
  1999. if session.Mode == "offline" {
  2000. statusColor = rl.White
  2001. statusText = "Status: [ " + strconv.FormatUint(wHeight, 10) + " / 0 ] OFFLINE MODE"
  2002. progressNow = 0
  2003. }
  2004. } else {
  2005. progressNow = 0
  2006. statusColor = rl.White
  2007. statusText = "Ready"
  2008. }
  2009. }
  2010. func toggleSidebar() {
  2011. rl.DrawRectangle(rl.GetScreenWidth() - 400, 0, 400, rl.GetScreenHeight() - 30, rl.Black)
  2012. }
  2013. func toggleMode() {
  2014. statusText = "Switching network mode..."
  2015. if session.Mode == "local" {
  2016. session.Mode = "remote"
  2017. if session.Network == "Mainnet" {
  2018. session.Daemon = config.RemoteDaemon
  2019. wallet.SetDaemonAddress(config.RemoteDaemon)
  2020. } else {
  2021. session.Daemon = config.RemoteDaemonTestnet
  2022. wallet.SetDaemonAddress(config.RemoteDaemonTestnet)
  2023. }
  2024. } else {
  2025. session.Mode = "local"
  2026. if session.Network == "Mainnet" {
  2027. session.Daemon = config.LocalDaemon
  2028. wallet.SetDaemonAddress(config.LocalDaemon)
  2029. } else {
  2030. session.Daemon = config.LocalDaemonTestnet
  2031. wallet.SetDaemonAddress(config.LocalDaemonTestnet)
  2032. }
  2033. }
  2034. session.RPCServer = false
  2035. wallet.Stop_RPC_Server()
  2036. wallet.SetOfflineMode()
  2037. session.Syncing = false
  2038. wallet.Close_Encrypted_Wallet()
  2039. wallet = nil
  2040. windowIndex = 2.1
  2041. }
  2042. // Rescan wallet
  2043. func rescan_bc(wallet *walletapi.Wallet, height uint64) {
  2044. wallet.Clean()
  2045. wallet.Rescan_From_Height(0)
  2046. }
  2047. // Provide the mask for a password
  2048. func passwordMask(password string) (bool, string) {
  2049. runes := []rune(password)
  2050. if len(runes) == 0 {
  2051. return false, ""
  2052. }
  2053. mask := ""
  2054. for i := 0; i < len(runes); i++ {
  2055. mask += "*"
  2056. }
  2057. return true, mask
  2058. }
  2059. // Mask text (UI fix)
  2060. func textMask(text string) (bool, string) {
  2061. runes := []rune(text)
  2062. if len(runes) == 0 {
  2063. return false, ""
  2064. }
  2065. mask := ""
  2066. for i := 0; i < len(runes); i++ {
  2067. mask += string(runes[i])
  2068. }
  2069. return true, mask
  2070. }
  2071. // Convert a string to uint64
  2072. func stoUint64(temp string) uint64 {
  2073. conversion, _ := strconv.ParseUint(string(temp), 10, 64)
  2074. return conversion
  2075. }
  2076. // Display seed to the user in his/her preferred language
  2077. func display_seed(wallet *walletapi.Wallet) string {
  2078. seed := wallet.GetSeed()
  2079. return seed
  2080. }
  2081. // Display spend key
  2082. func display_spend_key(wallet *walletapi.Wallet) (secret crypto.Key, public crypto.Key) {
  2083. keys := wallet.Get_Keys()
  2084. if !account.ViewOnly {
  2085. secret = keys.Spendkey_Secret
  2086. }
  2087. public = keys.Spendkey_Public
  2088. return secret, public
  2089. }
  2090. // Display view key
  2091. func display_view_key(wallet *walletapi.Wallet) (secret crypto.Key, public crypto.Key) {
  2092. keys := wallet.Get_Keys()
  2093. secret = keys.Viewkey_Secret
  2094. public = keys.Viewkey_Public
  2095. return secret, public
  2096. }
  2097. // Display wallet view only Keys to create a watchable view only mode
  2098. func display_viewwallet_key(wallet *walletapi.Wallet) (view string) {
  2099. view = wallet.GetViewWalletKey()
  2100. return view
  2101. }
  2102. // Trim wallet address
  2103. func trim_address(address string) string {
  2104. length := len(address)
  2105. prefix := address[0:4]
  2106. last := address[(length - 16):length]
  2107. trim := prefix + "..." + last
  2108. return trim
  2109. }
  2110. func toggleTerminal(large rl.Font, small rl.Font) {
  2111. if windowIndex == 5.0 {
  2112. rl.GuiSetFont(large)
  2113. command = ""
  2114. windowIndex = 0
  2115. } else {
  2116. rl.GuiSetFont(small)
  2117. command = ""
  2118. windowIndex = 5.0
  2119. }
  2120. }
  2121. func openURL(url string) {
  2122. var err error
  2123. switch runtime.GOOS {
  2124. case "linux":
  2125. err = exec.Command("xdg-open", url).Start()
  2126. case "windows":
  2127. err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
  2128. case "darwin":
  2129. err = exec.Command("open", url).Start()
  2130. default:
  2131. err = exec.Command("open", url).Start()
  2132. }
  2133. if err != nil {
  2134. log.Warnf("Error opening URL: %s", err)
  2135. }
  2136. }
  2137. // handles the output after building tx, takes feedback, confirms or relays tx
  2138. func build_relay_transaction(tx *transaction.Transaction, inputs []uint64, input_sum uint64, change uint64, err error, offline_tx bool, amount_list []uint64) bool {
  2139. if err != nil {
  2140. log.Warnf("Error building transaction: %s", err)
  2141. return false
  2142. }
  2143. transfer.Inputs = append(transfer.Inputs, uint64(len(inputs)))
  2144. transfer.InputSum = input_sum
  2145. transfer.Change = change
  2146. transfer.Size = float32(len(tx.Serialize()))/1024.0
  2147. transfer.Fees = tx.RctSignature.Get_TX_Fee()
  2148. transfer.TX = tx
  2149. amount := uint64(0)
  2150. for i := range amount_list {
  2151. amount += amount_list[i]
  2152. }
  2153. if input_sum != (amount + change + tx.RctSignature.Get_TX_Fee()) {
  2154. return false
  2155. }
  2156. return true
  2157. }
  2158. func resetTransfer() {
  2159. transfer.rAddress = nil
  2160. transfer.PaymentID = nil
  2161. transfer.Amount = 0
  2162. transfer.Fees = 0
  2163. transfer.TX = nil
  2164. //transfer.TXID = transfer.TX.GetHash()
  2165. transfer.Size = 0
  2166. transfer.Status = ""
  2167. transfer.Inputs = nil
  2168. transfer.InputSum = 0
  2169. transfer.Change = 0
  2170. transfer.Relay = false
  2171. transfer.OfflineTX = false
  2172. transfer.Filename = ""
  2173. loginPassword = ""
  2174. amountString = ""
  2175. pidString = ""
  2176. receiverString = ""
  2177. checked = false
  2178. }
  2179. func clearVars() {
  2180. createAccountFilename = ""
  2181. restoreFilename = ""
  2182. createAccountPassword = ""
  2183. restorePassword = ""
  2184. restoreHex = ""
  2185. tempPassword = ""
  2186. seed = ""
  2187. address_s = ""
  2188. passwordError = false
  2189. fileError = false
  2190. createAccountCompleted = false
  2191. }
  2192. func reloadConfig() {
  2193. session.Network = config.DefaultNetwork
  2194. session.RPCAddress = config.RPCAddress
  2195. session.RPCAuth = config.RPCAuth
  2196. if config.DefaultNetwork == "Mainnet" {
  2197. if session.Mode == "remote" {
  2198. session.Daemon = config.RemoteDaemon
  2199. } else {
  2200. session.Daemon = config.LocalDaemon
  2201. }
  2202. session.Color = cmdGreen
  2203. globals.Arguments["--testnet"] = false
  2204. } else {
  2205. if session.Mode == "remote" {
  2206. session.Daemon = config.RemoteDaemonTestnet
  2207. } else {
  2208. session.Daemon = config.LocalDaemonTestnet
  2209. }
  2210. session.Color = cmdBlue
  2211. globals.Arguments["--testnet"] = true
  2212. }
  2213. globals.Initialize()
  2214. }
  2215. /*
  2216. func choose_seed_language(choice int) string {
  2217. languages := mnemonics.Language_List()
  2218. for i := range languages {
  2219. fmt.Fprintf(l.Stderr(), "\033[1m%2d:\033[0m %s\n", i, languages[i])
  2220. }
  2221. if s, err := strconv.Atoi(choice); err == nil {
  2222. choice = s
  2223. }
  2224. for i := range languages { // if user gave any wrong or ot of range choice, choose english
  2225. if choice == i {
  2226. return languages[choice]
  2227. }
  2228. }
  2229. // if no match , return English
  2230. return "English"
  2231. }
  2232. */
  2233. /*
  2234. // Word-wrapping for raylib textboxes based on line length
  2235. func wordWrap(text string, length int) ([]string, int) {
  2236. tmpText := text
  2237. var segments []string
  2238. runes := []rune(tmpText)
  2239. if len(runes) == 0 {
  2240. return nil, 0
  2241. }
  2242. for i := 0; i < len(runes); i += length {
  2243. n := i + length
  2244. if n > len(runes) {
  2245. n = len(runes)
  2246. }
  2247. segments = append(segments, string(runes[i:n]))
  2248. }
  2249. return segments, len(segments)
  2250. }
  2251. */