aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/logpage.go
diff options
context:
space:
mode:
authorAlexander Neumann <alexander.neumann@picos-software.com>2019-04-24 15:29:38 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2019-04-24 15:42:51 +0200
commit4a0ee91473619db8a2c8a100758f1c24c39e7e39 (patch)
tree5df163a3e81b948fdf05f26429739d12b41ffc2e /ui/logpage.go
parentui: programmatically compute colors (diff)
downloadwireguard-windows-4a0ee91473619db8a2c8a100758f1c24c39e7e39.tar.xz
wireguard-windows-4a0ee91473619db8a2c8a100758f1c24c39e7e39.zip
ui: use tabs in main window and refactor tunnels ui and log dialog into tab pages
requires https://github.com/lxn/walk/commit/edb74ee350e9585ddd212acad445ec383950f2cc for status image background Signed-off-by: Alexander Neumann <alexander.neumann@picos-software.com>
Diffstat (limited to 'ui/logpage.go')
-rw-r--r--ui/logpage.go184
1 files changed, 184 insertions, 0 deletions
diff --git a/ui/logpage.go b/ui/logpage.go
new file mode 100644
index 00000000..4f39ae87
--- /dev/null
+++ b/ui/logpage.go
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package ui
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/lxn/win"
+
+ "github.com/lxn/walk"
+ "golang.zx2c4.com/wireguard/windows/ringlogger"
+)
+
+func NewLogPage(logger *ringlogger.Ringlogger) (*LogPage, error) {
+ lp := &LogPage{logger: logger}
+
+ var disposables walk.Disposables
+ defer disposables.Treat()
+
+ var err error
+
+ if lp.TabPage, err = walk.NewTabPage(); err != nil {
+ return nil, err
+ }
+ disposables.Add(lp)
+
+ lp.Disposing().Attach(func() {
+ lp.model.quit <- true
+ })
+
+ lp.SetTitle("Log")
+ lp.SetLayout(walk.NewVBoxLayout())
+ lp.Layout().SetMargins(walk.Margins{18, 18, 18, 18})
+
+ if lp.logView, err = walk.NewTableView(lp); err != nil {
+ return nil, err
+ }
+ lp.logView.SetAlternatingRowBGColor(walk.Color(win.GetSysColor(win.COLOR_BTNFACE)))
+ lp.logView.SetLastColumnStretched(true)
+
+ stampCol := walk.NewTableViewColumn()
+ stampCol.SetName("Stamp")
+ stampCol.SetTitle("Time")
+ stampCol.SetFormat("2006-01-02 15:04:05.000")
+ stampCol.SetWidth(150)
+ lp.logView.Columns().Add(stampCol)
+
+ msgCol := walk.NewTableViewColumn()
+ msgCol.SetName("Line")
+ msgCol.SetTitle("Log message")
+ lp.logView.Columns().Add(msgCol)
+
+ lp.model = newLogModel(lp, logger)
+ lp.logView.SetModel(lp.model)
+
+ buttonsContainer, err := walk.NewComposite(lp)
+ if err != nil {
+ return nil, err
+ }
+ buttonsContainer.SetLayout(walk.NewHBoxLayout())
+ buttonsContainer.Layout().SetMargins(walk.Margins{0, 12, 0, 0})
+
+ walk.NewHSpacer(buttonsContainer)
+
+ saveButton, err := walk.NewPushButton(buttonsContainer)
+ if err != nil {
+ return nil, err
+ }
+ saveButton.SetText("Save")
+ saveButton.Clicked().Attach(lp.onSaveButtonClicked)
+
+ disposables.Spare()
+
+ return lp, nil
+}
+
+type LogPage struct {
+ *walk.TabPage
+ logView *walk.TableView
+ logger *ringlogger.Ringlogger
+ model *logModel
+}
+
+func (lp *LogPage) isAtBottom() bool {
+ return lp.logView.ItemVisible(len(lp.model.items) - 1)
+}
+
+func (lp *LogPage) scrollToBottom() {
+ lp.logView.EnsureItemVisible(len(lp.model.items) - 1)
+}
+
+func (lp *LogPage) onSaveButtonClicked() {
+ fd := walk.FileDialog{
+ Filter: "Text Files (*.txt)|*.txt|All Files (*.*)|*.*",
+ FilePath: fmt.Sprintf("wireguard-log-%s.txt", time.Now().Format("2006-01-02T150405")),
+ Title: "Export log to file",
+ }
+
+ form := lp.Form()
+
+ if ok, _ := fd.ShowSave(form); !ok {
+ return
+ }
+
+ extensions := []string{".log", ".txt"}
+ if fd.FilterIndex < 3 && !strings.HasSuffix(fd.FilePath, extensions[fd.FilterIndex-1]) {
+ fd.FilePath = fd.FilePath + extensions[fd.FilterIndex-1]
+ }
+
+ writeFileWithOverwriteHandling(form, fd.FilePath, func(file *os.File) error {
+ if _, err := lp.logger.WriteTo(file); err != nil {
+ return fmt.Errorf("exportLog: Ringlogger.WriteTo failed: %v", err)
+ }
+
+ return nil
+ })
+}
+
+type logModel struct {
+ walk.ReflectTableModelBase
+ lp *LogPage
+ quit chan bool
+ logger *ringlogger.Ringlogger
+ items []ringlogger.FollowLine
+}
+
+func newLogModel(lp *LogPage, logger *ringlogger.Ringlogger) *logModel {
+ mdl := &logModel{lp: lp, quit: make(chan bool), logger: logger}
+ var lastCursor uint32
+ mdl.items, lastCursor = logger.FollowFromCursor(ringlogger.CursorAll)
+
+ var lastStamp time.Time
+ if len(mdl.items) > 0 {
+ lastStamp = mdl.items[len(mdl.items)-1].Stamp
+ }
+
+ go func() {
+ ticker := time.NewTicker(time.Second)
+
+ for {
+ select {
+ case <-ticker.C:
+ items, cursor := mdl.logger.FollowFromCursor(ringlogger.CursorAll)
+
+ var stamp time.Time
+ if len(items) > 0 {
+ stamp = items[len(items)-1].Stamp
+ }
+
+ if cursor != lastCursor || stamp.After(lastStamp) {
+ lastCursor = cursor
+ lastStamp = stamp
+
+ mdl.lp.Synchronize(func() {
+ isAtBottom := mdl.lp.isAtBottom()
+
+ mdl.items = items
+ mdl.PublishRowsReset()
+
+ if isAtBottom {
+ mdl.lp.scrollToBottom()
+ }
+ })
+ }
+
+ case <-mdl.quit:
+ ticker.Stop()
+ break
+ }
+ }
+ }()
+
+ return mdl
+}
+
+func (mdl *logModel) Items() interface{} {
+ return mdl.items
+}