package main import ( "fmt" "os" "path/filepath" "slices" "strings" "time" "github.com/BurntSushi/toml" "github.com/ying32/govcl/vcl" "github.com/ying32/govcl/vcl/types" ) type TMainForm struct { *vcl.TForm MainMenu *vcl.TMainMenu ChangePasswordMenuItem *vcl.TMenuItem AboutMenuItem *vcl.TMenuItem PageControl *vcl.TPageControl TabSheet1 *vcl.TTabSheet GroupBox *vcl.TGroupBox ConfigLabel *vcl.TLabel ConfigComboBox *vcl.TComboBox SqlFileLabel *vcl.TLabel SqlFileComboBox *vcl.TComboBox ExportFormatLabel *vcl.TLabel ExportFormatComboBox *vcl.TComboBox CharsetLabel *vcl.TLabel CharsetComboBox *vcl.TComboBox ExportPathLabel *vcl.TLabel ExportPathPicker *vcl.TDirectoryEdit BottomPanel *vcl.TPanel LaunchButton *vcl.TBitBtn TabSheet2 *vcl.TTabSheet ServerListView *vcl.TListView c1 *vcl.TListColumn c2 *vcl.TListColumn StatusBar *vcl.TStatusBar p1 *vcl.TStatusPanel p2 *vcl.TStatusPanel } var mainForm *TMainForm func (f *TMainForm) OnFormCreate(sender vcl.IObject) { f.SetWidth(800) f.SetHeight(400) f.Constraints().SetMinHeight(400) // to prevent wrong ordering of widgets for small windows sizes f.SetPosition(types.PoDesktopCenter) f.SetCaption("OMQ") f.SetDoubleBuffered(true) f.MainMenu = vcl.NewMainMenu(f) f.ChangePasswordMenuItem = vcl.NewMenuItem(f) f.MainMenu.Items().Add(f.ChangePasswordMenuItem) f.ChangePasswordMenuItem.SetCaption("Изменить логин/пароль") f.ChangePasswordMenuItem.SetBitmap(getImageBitmap("img/change_password.png")) f.ChangePasswordMenuItem.SetOnClick(f.changePassword) f.AboutMenuItem = vcl.NewMenuItem(f) f.MainMenu.Items().Add(f.AboutMenuItem) f.AboutMenuItem.SetCaption("О программе") f.AboutMenuItem.SetBitmap(getImageBitmap("img/information.png")) f.AboutMenuItem.SetOnClick(func(sender vcl.IObject) { vcl.MessageDlg("Программа выгрузки результатов SQL-запросов с нескольких серверов баз данных Oracle.", types.MtInformation) }) f.PageControl = vcl.NewPageControl(f) f.PageControl.SetParent(f) f.PageControl.SetAlign(types.AlClient) f.TabSheet1 = vcl.NewTabSheet(f.PageControl) f.TabSheet1.SetParent(f.PageControl) f.TabSheet1.SetTabVisible(false) f.GroupBox = vcl.NewGroupBox(f) f.GroupBox.SetParent(f.TabSheet1) f.GroupBox.SetAlign(types.AlClient) f.GroupBox.SetCaption("Параметры запроса") // TODO: добавить AutoSize f.ConfigLabel = vcl.NewLabel(f) f.ConfigLabel.SetParent(f.GroupBox) f.ConfigLabel.SetCaption("Настройки серверов") f.ConfigLabel.SetTop(0) f.ConfigLabel.BorderSpacing().SetTop(8) f.ConfigLabel.BorderSpacing().SetLeft(8) f.ConfigLabel.BorderSpacing().SetRight(8) f.ConfigLabel.SetAlign(types.AlTop) f.ConfigComboBox = vcl.NewComboBox(f) f.ConfigComboBox.SetParent(f.GroupBox) f.ConfigComboBox.SetAlign(types.AlTop) f.ConfigComboBox.BorderSpacing().SetTop(2) f.ConfigComboBox.BorderSpacing().SetLeft(8) f.ConfigComboBox.BorderSpacing().SetRight(8) f.ConfigComboBox.SetTop(100) f.ConfigComboBox.SetStyle(types.CsDropDownList) f.SqlFileLabel = vcl.NewLabel(f) f.SqlFileLabel.SetParent(f.GroupBox) f.SqlFileLabel.SetCaption("SQL-скрипт") f.SqlFileLabel.SetAlign(types.AlTop) f.SqlFileLabel.BorderSpacing().SetTop(8) f.SqlFileLabel.SetTop(200) f.SqlFileLabel.BorderSpacing().SetLeft(8) f.SqlFileLabel.BorderSpacing().SetRight(8) f.SqlFileComboBox = vcl.NewComboBox(f) f.SqlFileComboBox.SetParent(f.GroupBox) f.SqlFileComboBox.SetAlign(types.AlTop) f.SqlFileComboBox.BorderSpacing().SetTop(2) f.SqlFileComboBox.BorderSpacing().SetLeft(8) f.SqlFileComboBox.BorderSpacing().SetRight(8) f.SqlFileComboBox.SetTop(300) f.SqlFileComboBox.SetStyle(types.CsDropDownList) f.ExportFormatLabel = vcl.NewLabel(f) f.ExportFormatLabel.SetParent(f.GroupBox) f.ExportFormatLabel.SetCaption("Формат выгрузки") f.ExportFormatLabel.SetAlign(types.AlTop) f.ExportFormatLabel.SetTop(400) f.ExportFormatLabel.BorderSpacing().SetTop(8) f.ExportFormatLabel.BorderSpacing().SetLeft(8) f.ExportFormatLabel.BorderSpacing().SetRight(8) f.ExportFormatComboBox = vcl.NewComboBox(f) f.ExportFormatComboBox.SetParent(f.GroupBox) f.ExportFormatComboBox.SetAlign(types.AlTop) f.ExportFormatComboBox.BorderSpacing().SetTop(2) f.ExportFormatComboBox.BorderSpacing().SetLeft(8) f.ExportFormatComboBox.BorderSpacing().SetRight(8) f.ExportFormatComboBox.SetTop(500) f.ExportFormatComboBox.SetStyle(types.CsDropDownList) f.ExportFormatComboBox.SetOnChange(f.OnExportFormatComboBoxChange) f.CharsetLabel = vcl.NewLabel(f) f.CharsetLabel.SetParent(f.GroupBox) f.CharsetLabel.SetCaption("Кодировка CSV-файла") f.CharsetLabel.SetAlign(types.AlTop) f.CharsetLabel.SetTop(600) f.CharsetLabel.BorderSpacing().SetTop(8) f.CharsetLabel.BorderSpacing().SetLeft(8) f.CharsetLabel.BorderSpacing().SetRight(8) f.CharsetComboBox = vcl.NewComboBox(f) f.CharsetComboBox.SetParent(f.GroupBox) f.CharsetComboBox.SetAlign(types.AlTop) f.CharsetComboBox.BorderSpacing().SetTop(2) f.CharsetComboBox.BorderSpacing().SetLeft(8) f.CharsetComboBox.BorderSpacing().SetRight(8) f.CharsetComboBox.SetTop(700) f.CharsetComboBox.SetStyle(types.CsDropDownList) f.ExportPathLabel = vcl.NewLabel(f) f.ExportPathLabel.SetParent(f.GroupBox) f.ExportPathLabel.SetCaption("Путь сохранения результата") f.ExportPathLabel.SetAlign(types.AlTop) f.ExportPathLabel.SetTop(800) f.ExportPathLabel.BorderSpacing().SetTop(8) f.ExportPathLabel.BorderSpacing().SetLeft(8) f.ExportPathLabel.BorderSpacing().SetRight(8) f.ExportPathPicker = vcl.NewDirectoryEdit(f) f.ExportPathPicker.SetParent(f.GroupBox) f.ExportPathPicker.SetAlign(types.AlTop) f.ExportPathPicker.BorderSpacing().SetTop(2) f.ExportPathPicker.BorderSpacing().SetLeft(8) f.ExportPathPicker.BorderSpacing().SetRight(8) f.ExportPathPicker.SetTop(900) f.ExportPathPicker.SetFlat(true) f.BottomPanel = vcl.NewPanel(f) f.BottomPanel.SetParent(f.TabSheet1) f.BottomPanel.SetAlign(types.AlBottom) f.BottomPanel.SetHeight(48) f.BottomPanel.SetBevelOuter(types.BvNone) f.LaunchButton = vcl.NewBitBtn(f) f.LaunchButton.SetParent(f.BottomPanel) f.LaunchButton.SetCaption("Запуск") f.LaunchButton.SetAlign(types.AlRight) f.LaunchButton.SetWidth(96) f.LaunchButton.BorderSpacing().SetAround(8) f.LaunchButton.SetOnClick(f.OnLaunchButtonClick) f.LaunchButton.SetGlyph(getImageBitmap("img/bullet_go.png")) f.TabSheet2 = vcl.NewTabSheet(f.PageControl) f.TabSheet2.SetParent(f.PageControl) f.TabSheet2.SetTabVisible(false) f.ServerListView = vcl.NewListView(f) f.ServerListView.SetParent(f.TabSheet2) f.ServerListView.SetAlign(types.AlClient) f.ServerListView.SetViewStyle(types.VsReport) f.ServerListView.SetBorderStyle(types.BsNone) f.ServerListView.SetReadOnly(true) f.ServerListView.SetRowSelect(true) f.c1 = f.ServerListView.Columns().Add() f.c1.SetCaption("Сервер") f.c1.SetAutoSize(true) f.c2 = f.ServerListView.Columns().Add() f.c2.SetCaption("Статус") f.StatusBar = vcl.NewStatusBar(f) f.StatusBar.SetParent(f) f.StatusBar.SetSimplePanel(false) f.p1 = f.StatusBar.Panels().Add() f.p2 = f.StatusBar.Panels().Add() f.SetOnResize(f.OnFormResize) // Servers file list ------------------------------------------------------- configs, _ := filepath.Glob(filepath.Join(CONFIG_FILES_DIR, fmt.Sprintf("*.%s", CONFIG_FILE_EXT))) for i := range configs { configs[i] = strings.TrimSuffix(filepath.Base(configs[i]), "."+CONFIG_FILE_EXT) } for _, v := range configs { f.ConfigComboBox.Items().Add(v) } if len(configs) > 0 { f.ConfigComboBox.SetItemIndex(0) } // ------------------------------------------------------------------------- // SQL-files list ---------------------------------------------------------- files, _ := filepath.Glob(filepath.Join(SQL_FILES_DIR, fmt.Sprintf("*.%s", SQL_FILE_EXT))) for i := range files { files[i] = strings.TrimSuffix(filepath.Base(files[i]), "."+SQL_FILE_EXT) } for _, v := range files { f.SqlFileComboBox.Items().Add(v) } if len(configs) > 0 { f.SqlFileComboBox.SetItemIndex(0) } // ------------------------------------------------------------------------- // File formats ------------------------------------------------------------ for _, v := range []string{string(ExportFormatExcel), string(ExportFormatCsv), string(ExportFormatCsvZip)} { f.ExportFormatComboBox.Items().Add(v) } f.ExportFormatComboBox.SetItemIndex(0) // Charsets ---------------------------------------------------------------- for _, v := range []string{string(EncodingWin1251), string(EncodingUtf8)} { f.CharsetComboBox.Items().Add(v) } f.CharsetComboBox.SetItemIndex(0) f.OnExportFormatComboBoxChange(nil) // ------------------------------------------------------------------------- } func (f *TMainForm) OnFormResize(sender vcl.IObject) { if f.Width() < 320 { f.StatusBar.Panels().Items(0).SetWidth(f.Width() / 2) } else { f.StatusBar.Panels().Items(0).SetWidth(f.Width() - 160) } f.ServerListView.Columns().Items(1).SetWidth( f.ServerListView.Width() - f.ServerListView.Columns().Items(0).Width()) } func (f *TMainForm) OnLaunchButtonClick(sender vcl.IObject) { f.MainMenu.Free() job := &Job{ configFilePath: filepath.Join(CONFIG_FILES_DIR, f.ConfigComboBox.Text()) + "." + CONFIG_FILE_EXT, scriptFilePath: filepath.Join(SQL_FILES_DIR, f.SqlFileComboBox.Text()) + "." + SQL_FILE_EXT, exportFileFormat: ExportFormat(f.ExportFormatComboBox.Text()), encoding: Encoding(f.CharsetComboBox.Text()), exportPath: f.ExportPathPicker.Directory()} err := job.init() if err != nil { vcl.MessageDlg(err.Error(), types.MtError) return } for _, server := range job.config.Servers { item := f.ServerListView.Items().Add() item.SetCaption(server.Name) item.SubItems().Add("") } f.PageControl.SetPageIndex(1) go func() { for !job.isFinished { f.UpdateStatus(job) time.Sleep(UI_UPDATE_PERIOD) } }() go func() { err = job.launch() f.UpdateStatus(job) if err != nil { vcl.ThreadSync(func() { vcl.MessageDlg(err.Error(), types.MtError) f.Close() }) } else { vcl.ThreadSync(func() { vcl.MessageDlg("Завершено.", types.MtInformation) f.Close() }) } }() } func (f *TMainForm) OnExportFormatComboBoxChange(sender vcl.IObject) { if slices.Contains([]string{string(ExportFormatCsv), string(ExportFormatCsvZip)}, f.ExportFormatComboBox.Text()) { f.CharsetComboBox.SetEnabled(true) } else { f.CharsetComboBox.SetEnabled(false) } } func (f *TMainForm) UpdateStatus(job *Job) { vcl.ThreadSync(func() { f.OnFormResize(nil) f.StatusBar.Panels().Items(0).SetText(job.status) f.StatusBar.Panels().Items(1).SetText(fmt.Sprintf("%d строк", job.exportedRows)) for i, v := range job.config.Servers { s := []string{v.status} if v.err != nil { s = append(s, v.err.Error()) } f.ServerListView.Items().Item(int32(i)).SubItems().SetText(strings.Join(s, ": ")) } }) } func (f *TMainForm) changePassword(sender vcl.IObject) { changePasswordForm := new(TConfigEditorForm) vcl.Application.CreateForm(&changePasswordForm) if changePasswordForm.ShowModal() != types.IdOK { return } newLogin := changePasswordForm.loginEdit.Text() newPassword := changePasswordForm.passwordEdit.Text() changePasswordForm.Free() configFilePath := filepath.Join(CONFIG_FILES_DIR, f.ConfigComboBox.Text()) + "." + CONFIG_FILE_EXT config, err := loadConfig(configFilePath) if err != nil { vcl.MessageDlg(err.Error(), types.MtError) return } for _, server := range config.Servers { server.Login = newLogin server.Password = newPassword } file, err := os.Create(configFilePath) if err != nil { vcl.MessageDlg(err.Error(), types.MtError) return } err = toml.NewEncoder(file).Encode(config) if err != nil { vcl.MessageDlg(err.Error(), types.MtError) file.Close() return } err = file.Close() if err != nil { vcl.MessageDlg(err.Error(), types.MtError) return } vcl.MessageDlg("Логин/пароль успешно изменены.", types.MtInformation) }