func (f *frontend) Prompt(title, folder string, flags int) []string { w := f.windows[backend.GetEditor().ActiveWindow()] obj := w.qw.ObjectByName("fileDialog") obj.Set("title", title) obj.Set("folder", "file://"+folder) obj.Set("selectExisting", flags&backend.PROMPT_SAVE_AS == 0) obj.Set("selectFolder", flags&backend.PROMPT_ONLY_FOLDER == 1) obj.Set("selectMultiple", flags&backend.PROMPT_SELECT_MULTIPLE == 1) f.promptWaitGroup.Add(1) obj.Call("open") f.promptWaitGroup.Wait() if f.promptResult != "accepted" { return nil } res := obj.List("fileUrls") files := make([]string, res.Len()) res.Convert(&files) for i, file := range files { if file[:7] == "file://" { files[i] = file[7:] } } log.Fine("Selected %s files", files) return files }
func (f *frontend) message(text string, icon, btns int) string { cbs := make(map[string]int) if btns&okButton != 0 { cbs["accepted"] = 1 } if btns&cancelButton != 0 { cbs["rejected"] = 0 } w := f.windows[backend.GetEditor().ActiveWindow()] obj := w.qw.ObjectByName("messageDialog") obj.Set("text", text) obj.Set("icon", icon) obj.Set("standardButtons", btns) f.promptWaitGroup.Add(1) obj.Call("open") f.promptWaitGroup.Wait() log.Fine("returning %d from dialog", f.promptResult) return f.promptResult }
func (f *frontend) loop() (err error) { ed := backend.GetEditor() // TODO: As InitCallback doc says initiation code to be deferred until // after the UI is up and running. but because we dont have any // scheme we are initing editor before the UI comes up. ed.Init() ed.SetDefaultPath("../packages/Default") ed.SetUserPath("../packages/User") ed.SetClipboardFuncs(clipboard.WriteAll, clipboard.ReadAll) // Some packages(e.g Vintageos) need available window and view at start // so we need at least one window and view before loading packages. // Sublime text also has available window view on startup w := ed.NewWindow() w.NewFile() ed.AddPackagesPath("../packages") ed.SetFrontend(f) ed.LogInput(false) ed.LogCommands(false) c := ed.Console() f.Console = newView(c) c.AddObserver(f.Console) c.AddObserver(f) var ( engine *qml.Engine component qml.Object // WaitGroup keeping track of open windows wg sync.WaitGroup ) // create and setup a new engine, destroying // the old one if one exists. // // This is needed to re-load qml files to get // the new file contents from disc as otherwise // the old file would still be what is referenced. newEngine := func() (err error) { if engine != nil { log.Debug("calling destroy") // TODO(.): calling this appears to make the editor *very* crash-prone, just let it leak for now // engine.Destroy() engine = nil } log.Debug("calling newEngine") engine = qml.NewEngine() engine.On("quit", f.Quit) log.Fine("setvar frontend") engine.Context().SetVar("frontend", f) log.Fine("loading %s", qmlWindowFile) component, err = engine.LoadFile(qmlWindowFile) return } if err := newEngine(); err != nil { log.Error("Error on creating new engine: %s", err) panic(err) } addWindow := func(bw *backend.Window) { w := newWindow(bw) f.windows[bw] = w w.launch(&wg, component) } backend.OnNew.Add(f.onNew) backend.OnClose.Add(f.onClose) backend.OnLoad.Add(f.onLoad) backend.OnSelectionModified.Add(f.onSelectionModified) backend.OnNewWindow.Add(addWindow) backend.OnStatusChanged.Add(f.onStatusChanged) // we need to add windows and views that are added before we registered // actions for OnNewWindow and OnNew events for _, w := range ed.Windows() { addWindow(w) for _, v := range w.Views() { f.onNew(v) f.onLoad(v) } } defer func() { fmt.Println(util.Prof) }() // The rest of code is related to livereloading qml files // TODO: this doesnt work currently watch, err := fsnotify.NewWatcher() if err != nil { log.Error("Unable to create file watcher: %s", err) return } defer watch.Close() watch.Add("qml") defer watch.Remove("qml") reloadRequested := false waiting := false go func() { // reloadRequested = true // f.Quit() lastTime := time.Now() for { select { case ev := <-watch.Events: if time.Now().Sub(lastTime) < 1*time.Second { // quitting too frequently causes crashes lastTime = time.Now() continue } if strings.HasSuffix(ev.Name, ".qml") && ev.Op == fsnotify.Write && ev.Op != fsnotify.Chmod && !reloadRequested && waiting { reloadRequested = true f.Quit() lastTime = time.Now() } } } }() for { // Reset reload status reloadRequested = false log.Debug("Waiting for all windows to close") // wg would be the WaitGroup all windows belong to, so first we wait for // all windows to close. waiting = true wg.Wait() waiting = false log.Debug("All windows closed. reloadRequest: %v", reloadRequested) // then we check if there's a reload request in the pipe if !reloadRequested || len(f.windows) == 0 { // This would be a genuine exit; all windows closed by the user break } // *We* closed all windows because we want to reload freshly changed qml // files. for { log.Debug("Calling newEngine") if err := newEngine(); err != nil { // Reset reload status reloadRequested = false waiting = true log.Error(err) for !reloadRequested { // This loop allows us to re-try reloading // if there was an error in the file this time, // we just loop around again when we receive the next // reload request (ie on the next save of the file). time.Sleep(time.Second) } waiting = false continue } log.Debug("break") break } log.Debug("re-launching all windows") // Succeeded loading the file, re-launch all windows for _, w := range f.windows { w.launch(&wg, component) for _, bv := range w.Back().Views() { f.onNew(bv) f.onLoad(bv) } } } return }