236 lines
5.4 KiB
Go
236 lines
5.4 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"fyne.io/fyne/v2"
|
||
"fyne.io/fyne/v2/dialog"
|
||
"fyne.io/fyne/v2/widget"
|
||
)
|
||
|
||
// Вспомогательные методы
|
||
func (a *App) showError(title, message string) {
|
||
dialog.ShowError(fmt.Errorf(message), a.window)
|
||
a.statusLabel.SetText("❌ " + title)
|
||
}
|
||
|
||
func (a *App) showSuccess(title, message string) {
|
||
dialog.ShowInformation(title, message, a.window)
|
||
}
|
||
|
||
func (a *App) detectMimeType() string {
|
||
ext := strings.ToLower(filepath.Ext(a.fileName))
|
||
|
||
mimeTypes := map[string]string{
|
||
".txt": "text/plain",
|
||
".pdf": "application/pdf",
|
||
".jpg": "image/jpeg",
|
||
".jpeg": "image/jpeg",
|
||
".png": "image/png",
|
||
".zip": "application/zip",
|
||
".doc": "application/msword",
|
||
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||
}
|
||
|
||
if mime, ok := mimeTypes[ext]; ok {
|
||
return mime
|
||
}
|
||
return "application/octet-stream"
|
||
}
|
||
|
||
// // Это делал DeepSeek оно криво, но работает. Оставлю это так, потому что всё равно планирую не использовать больше fyne
|
||
type JournalEntryWidget struct {
|
||
widget.BaseWidget
|
||
event journalctlEntry
|
||
label *widget.Label
|
||
}
|
||
|
||
func NewJournalEntryWidget(event journalctlEntry) *JournalEntryWidget {
|
||
j := &JournalEntryWidget{
|
||
event: event,
|
||
}
|
||
j.ExtendBaseWidget(j)
|
||
return j
|
||
}
|
||
|
||
func (j *JournalEntryWidget) CreateRenderer() fyne.WidgetRenderer {
|
||
j.label = widget.NewLabel(j.formatEventText())
|
||
j.label.Wrapping = fyne.TextWrapBreak
|
||
j.label.Selectable = true
|
||
|
||
return widget.NewSimpleRenderer(j.label)
|
||
}
|
||
|
||
func (j *JournalEntryWidget) formatEventText() string {
|
||
return fmt.Sprintf("%s :: %s :: %s :: %s :: %s",
|
||
j.event.Timestamp, j.event.CommandLine, j.event.CommandName,
|
||
j.event.SyslogIdentifier, j.event.Message)
|
||
}
|
||
|
||
func (j *JournalEntryWidget) UpdateEvent(event journalctlEntry) {
|
||
j.event = event
|
||
if j.label != nil {
|
||
j.label.SetText(j.formatEventText())
|
||
}
|
||
j.Refresh()
|
||
}
|
||
|
||
//
|
||
//
|
||
//
|
||
//
|
||
|
||
// func (a *App) isTextFile() bool {
|
||
// ext := strings.ToLower(filepath.Ext(a.fileName))
|
||
// textExts := []string{".txt", ".csv", ".json", ".xml", ".yaml", ".yml", ".md", ".go", ".py", ".js"}
|
||
|
||
// for _, textExt := range textExts {
|
||
// if ext == textExt {
|
||
// return true
|
||
// }
|
||
// }ц=
|
||
|
||
// // Проверяем содержимое (простейшая проверка)
|
||
// if len(a.fileContent) == 0 {
|
||
// return false
|
||
// }
|
||
|
||
// // Если файл содержит null-байты, это скорее бинарный файл
|
||
// for _, b := range a.fileContent {
|
||
// if b == 0 {
|
||
// return false
|
||
// }
|
||
// }
|
||
|
||
// return true
|
||
// }
|
||
|
||
// type TextStats struct {
|
||
// lines int
|
||
// words int
|
||
// chars int
|
||
// bytes int
|
||
// }
|
||
|
||
// func (a *App) calculateTextStats() TextStats {
|
||
// if len(a.fileContent) == 0 {
|
||
// return TextStats{}
|
||
// }
|
||
|
||
// content := string(a.fileContent)
|
||
// stats := TextStats{
|
||
// bytes: len(a.fileContent),
|
||
// chars: len(content),
|
||
// lines: strings.Count(content, "\n") + 1,
|
||
// words: len(strings.Fields(content)),
|
||
// }
|
||
|
||
// return stats
|
||
// }
|
||
|
||
// Это делал DeepSeek оно криво, но работает. Оставлю это так, потому что всё равно планирую не использовать больше fyne
|
||
// Кастомный виджет для элемента списка с динамической высотой
|
||
type JournalListItem struct {
|
||
widget.BaseWidget
|
||
event journalctlEntry
|
||
label *widget.Label
|
||
}
|
||
|
||
func NewJournalListItem(event journalctlEntry) *JournalListItem {
|
||
j := &JournalListItem{
|
||
event: event,
|
||
}
|
||
j.ExtendBaseWidget(j)
|
||
return j
|
||
}
|
||
|
||
func (j *JournalListItem) CreateRenderer() fyne.WidgetRenderer {
|
||
j.label = widget.NewLabel(j.formatText())
|
||
j.label.Wrapping = fyne.TextWrapBreak
|
||
j.label.Selectable = true
|
||
|
||
return &journalListItemRenderer{
|
||
item: j,
|
||
label: j.label,
|
||
}
|
||
}
|
||
|
||
func (j *JournalListItem) formatText() string {
|
||
return fmt.Sprintf("%s :: %s :: %s :: %s :: %s",
|
||
j.event.Timestamp, j.event.CommandLine, j.event.CommandName,
|
||
j.event.SyslogIdentifier, j.event.Message)
|
||
}
|
||
|
||
func (j *JournalListItem) Update(event journalctlEntry) {
|
||
j.event = event
|
||
if j.label != nil {
|
||
j.label.SetText(j.formatText())
|
||
}
|
||
j.Refresh()
|
||
}
|
||
|
||
type journalListItemRenderer struct {
|
||
item *JournalListItem
|
||
label *widget.Label
|
||
}
|
||
|
||
func (r *journalListItemRenderer) Layout(size fyne.Size) {
|
||
// Рассчитываем высоту label на основе его содержимого
|
||
minSize := r.label.MinSize()
|
||
labelHeight := minSize.Height
|
||
|
||
// Если высота label больше 50, используем её, иначе минимальную
|
||
height := labelHeight
|
||
if height < 50 {
|
||
height = 50
|
||
}
|
||
|
||
r.label.Resize(fyne.NewSize(size.Width, height))
|
||
r.label.Move(fyne.NewPos(0, 0))
|
||
}
|
||
|
||
func (r *journalListItemRenderer) MinSize() fyne.Size {
|
||
minSize := r.label.MinSize()
|
||
if minSize.Height < 50 {
|
||
minSize.Height = 50
|
||
}
|
||
return minSize
|
||
}
|
||
|
||
func (r *journalListItemRenderer) Refresh() {
|
||
r.label.Refresh()
|
||
}
|
||
|
||
func (r *journalListItemRenderer) Objects() []fyne.CanvasObject {
|
||
return []fyne.CanvasObject{r.label}
|
||
}
|
||
|
||
func (r *journalListItemRenderer) Destroy() {}
|
||
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
|
||
// Вспомогательные функции
|
||
func formatFileSize(bytes int64) string {
|
||
const unit = 1024
|
||
if bytes < unit {
|
||
return fmt.Sprintf("%d Б", bytes)
|
||
}
|
||
div, exp := int64(unit), 0
|
||
for n := bytes / unit; n >= unit; n /= unit {
|
||
div *= unit
|
||
exp++
|
||
}
|
||
return fmt.Sprintf("%.1f %cБ", float64(bytes)/float64(div), "КМГТП"[exp])
|
||
}
|