gvcsfx 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/Gemfile +24 -0
- data/README.md +71 -0
- data/bin/gvcsfx +13 -0
- data/bin/gvcsfx.rb +97 -0
- data/config/warble.rb +212 -0
- data/fx/add_workspace.fxml +90 -0
- data/fx/diff.fxml +51 -0
- data/fx/float.fxml +29 -0
- data/fx/main.fxml +637 -0
- data/fx/show_text.fxml +50 -0
- data/fx/stash_select.fxml +59 -0
- data/gvcs_fx.rb +85 -0
- data/handlers/add_workspace_controller.rb +88 -0
- data/handlers/event_handler.rb +58 -0
- data/handlers/float_win_controller.rb +68 -0
- data/handlers/fx_alert.rb +120 -0
- data/handlers/listener.rb +59 -0
- data/handlers/main_win_controller.rb +191 -0
- data/handlers/notification.rb +113 -0
- data/handlers/show_text_controller.rb +38 -0
- data/handlers/stash_select_controller.rb +86 -0
- data/handlers/tab_branches.rb +312 -0
- data/handlers/tab_files.rb +27 -0
- data/handlers/tab_ignore_rules.rb +40 -0
- data/handlers/tab_logs.rb +222 -0
- data/handlers/tab_repos.rb +277 -0
- data/handlers/tab_state.rb +571 -0
- data/handlers/tab_tags.rb +224 -0
- data/handlers/tray.rb +128 -0
- data/handlers/workspace.rb +547 -0
- data/javafx_test.rb +81 -0
- data/lib/global.rb +56 -0
- data/lib/log_helper.rb +40 -0
- data/lib/store.rb +152 -0
- data/lib/version.rb +21 -0
- data/res/accept.png +0 -0
- data/res/cross.png +0 -0
- data/res/tick.png +0 -0
- data/res/version-control.png +0 -0
- data/res/warn.png +0 -0
- data/res/world.png +0 -0
- metadata +156 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
# Copyright (C) 2020 Chris Liaw <chrisliaw@antrapol.com>
|
2
|
+
# Author: Chris Liaw <chrisliaw@antrapol.com>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
module GvcsFx
|
20
|
+
module TabTags
|
21
|
+
|
22
|
+
class VCSTag
|
23
|
+
include Antrapol::ToolRack::ExceptionUtils
|
24
|
+
attr_accessor :key, :name, :date, :author, :subject
|
25
|
+
def formatted_date
|
26
|
+
if not_empty?(@date)
|
27
|
+
dt = rdate
|
28
|
+
dt.strftime("%d.%b.%Y (%a) %H:%M %z")
|
29
|
+
else
|
30
|
+
""
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# for sorting
|
35
|
+
def rdate
|
36
|
+
DateTime.strptime(@date,"%a %b %d %H:%M:%S %Y %z")
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{@name} [#{formatted_date} by #{@author}]"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def init_tab_tags
|
45
|
+
|
46
|
+
@lstTags.selection_model.selection_mode = javafx.scene.control.SelectionMode::SINGLE
|
47
|
+
|
48
|
+
@lstTags.add_event_handler(javafx.scene.input.MouseEvent::MOUSE_CLICKED, Proc.new do |evt|
|
49
|
+
if evt.button == javafx.scene.input.MouseButton::SECONDARY
|
50
|
+
# # right click on item
|
51
|
+
tags_ctxmenu.show(@lstTags, evt.screen_x, evt.screen_y)
|
52
|
+
elsif evt.button == javafx.scene.input.MouseButton::PRIMARY and evt.click_count == 2
|
53
|
+
# double click on item - view file
|
54
|
+
sel = @lstTags.selection_model.selected_items
|
55
|
+
if sel.length > 0
|
56
|
+
s = sel.first
|
57
|
+
st, res = @selWs.show_tag_detail(s.name)
|
58
|
+
if st
|
59
|
+
show_content_win("Tag Detail","Tag Detail", res)
|
60
|
+
else
|
61
|
+
prompt_error("Failed to load tag detail for tag '#{s}'. Error was : #{res}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end)
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def refresh_tab_tags
|
71
|
+
|
72
|
+
@lstTags.items.clear
|
73
|
+
|
74
|
+
dat = []
|
75
|
+
st, res = @selWs.all_tags
|
76
|
+
if st and not_empty?(res)
|
77
|
+
|
78
|
+
res.each_line do |e|
|
79
|
+
|
80
|
+
st2,res2 = @selWs.tag_info(e.strip)
|
81
|
+
if st2
|
82
|
+
|
83
|
+
res2.each_line do |r|
|
84
|
+
rr = r.split("|")
|
85
|
+
# extremely unreliable that depending on default
|
86
|
+
# format in tag_info
|
87
|
+
if rr.length == 4
|
88
|
+
@rec = rr
|
89
|
+
break
|
90
|
+
else
|
91
|
+
next
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# default format = %H|%ad|%an|%s
|
96
|
+
if not_empty?(@rec)
|
97
|
+
vt = VCSTag.new
|
98
|
+
vt.name = e.strip
|
99
|
+
vt.key = @rec[0]
|
100
|
+
vt.date = @rec[1]
|
101
|
+
vt.author = @rec[2]
|
102
|
+
vt.subject = @rec[3]
|
103
|
+
#vt.name = e[0]
|
104
|
+
#vt.date = e[1]
|
105
|
+
#vt.author = e[2]
|
106
|
+
#vt.subject = e[3]
|
107
|
+
dat << vt
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end # tag info line
|
112
|
+
|
113
|
+
# sort
|
114
|
+
dat = dat.sort do |a,b|
|
115
|
+
# oldest comes to the top
|
116
|
+
#a.rdate <=> b.rdate
|
117
|
+
# latest comes to the top
|
118
|
+
b.rdate <=> a.rdate
|
119
|
+
#case
|
120
|
+
#when a.rdate < b.rdate
|
121
|
+
# 1
|
122
|
+
#when a.rdate > b.rdate
|
123
|
+
# -1
|
124
|
+
#else
|
125
|
+
# a.rdate <=> b.rdate
|
126
|
+
#end
|
127
|
+
end
|
128
|
+
|
129
|
+
@lstTags.items.add_all(dat)
|
130
|
+
end
|
131
|
+
|
132
|
+
@txtNewTagName.clear
|
133
|
+
@txtNewTagNote.clear
|
134
|
+
|
135
|
+
end # refresh_tab_tags
|
136
|
+
|
137
|
+
def create_tag(evt)
|
138
|
+
tn = @txtNewTagName.text
|
139
|
+
if is_empty?(tn)
|
140
|
+
fx_alert_error("Tag name cannot be empty","Empty Tag Name", main_stage)
|
141
|
+
else
|
142
|
+
msg = @txtNewTagNote.text
|
143
|
+
tn = tn.gsub(" ","-")
|
144
|
+
st, res = @selWs.create_tag(tn,msg)
|
145
|
+
if st
|
146
|
+
set_success_gmsg("New tag '#{tn}' successfully created.")
|
147
|
+
refresh_tab_tags
|
148
|
+
else
|
149
|
+
prompt_error(res.strip, "New Tag Creation Failed", main_stage)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end # create_tag
|
153
|
+
|
154
|
+
def new_tag_keypressed(evt)
|
155
|
+
if evt.code == javafx.scene.input.KeyCode::ENTER
|
156
|
+
create_tag(nil)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def lstTags_keypressed(evt)
|
161
|
+
if evt.code == javafx.scene.input.KeyCode::DELETE
|
162
|
+
selTag = @lstTags.selection_model.selected_item
|
163
|
+
if not selTag.nil?
|
164
|
+
res = fx_alert_confirmation("Remove tag '#{selTag.name}'?", nil, "Confirmation to Remove Tag", main_stage)
|
165
|
+
if res == :ok
|
166
|
+
st, res = @selWs.delete_tag(selTag.name)
|
167
|
+
|
168
|
+
if st
|
169
|
+
set_success_gmsg("Tag '#{selTag.name}' successfully deleted.")
|
170
|
+
refresh_tab_tags
|
171
|
+
else
|
172
|
+
prompt_error("Failed to delete tag '#{selTag.name}'. Error was : #{res}")
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
#@selTag = @lstTags.selection_model.selected_item
|
179
|
+
end
|
180
|
+
|
181
|
+
def tagnote_keypressed(evt)
|
182
|
+
if evt.code == javafx.scene.input.KeyCode::ENTER
|
183
|
+
create_tag(nil)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def tags_ctxmenu
|
188
|
+
|
189
|
+
selTag = @lstTags.selection_model.selected_item
|
190
|
+
|
191
|
+
tagsCtxMenu = javafx.scene.control.ContextMenu.new
|
192
|
+
|
193
|
+
if not_empty?(selTag)
|
194
|
+
|
195
|
+
coMnuItm = javafx.scene.control.MenuItem.new("Checkout to new branch")
|
196
|
+
coMnuItm.on_action do |evt|
|
197
|
+
|
198
|
+
st, name = fx_alert_input("Check out to a branch","Check out tag '#{selTag.name}' into new branch", "New Branch Name *")
|
199
|
+
if st
|
200
|
+
if not_empty?(name)
|
201
|
+
sst, res = @selWs.checkout_tag(selTag.name, name)
|
202
|
+
if sst
|
203
|
+
set_success_gmsg(res)
|
204
|
+
refresh_tab_tags
|
205
|
+
else
|
206
|
+
prompt_error("Failed to checkout tag '#{selTag.name}' to new branch '#{name}'. Error was : #{res}")
|
207
|
+
end
|
208
|
+
else
|
209
|
+
prompt_error("Please provide a branch name")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
end # on_action do .. end
|
214
|
+
|
215
|
+
tagsCtxMenu.items.add(coMnuItm)
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
tagsCtxMenu
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
end
|
data/handlers/tray.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Copyright (C) 2020 Chris Liaw <chrisliaw@antrapol.com>
|
2
|
+
# Author: Chris Liaw <chrisliaw@antrapol.com>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
|
18
|
+
java_import java.awt.AWTException
|
19
|
+
java_import java.awt.Menu
|
20
|
+
java_import java.awt.MenuItem
|
21
|
+
java_import java.awt.PopupMenu
|
22
|
+
java_import java.awt.SystemTray
|
23
|
+
java_import java.awt.TrayIcon
|
24
|
+
java_import java.awt.event.ActionEvent
|
25
|
+
java_import java.awt.event.ActionListener
|
26
|
+
java_import javax.swing.ImageIcon
|
27
|
+
java_import javax.swing.JOptionPane
|
28
|
+
|
29
|
+
java_import javax.swing.JPopupMenu
|
30
|
+
java_import javax.swing.JMenu
|
31
|
+
java_import javax.swing.JMenuItem
|
32
|
+
|
33
|
+
require_relative "../lib/global"
|
34
|
+
require_relative '../lib/log_helper'
|
35
|
+
|
36
|
+
module GvcsFx
|
37
|
+
module Tray
|
38
|
+
include GvcsFx::LogHelper
|
39
|
+
|
40
|
+
def start_tray(app = nil)
|
41
|
+
|
42
|
+
if SystemTray.isSupported
|
43
|
+
|
44
|
+
begin
|
45
|
+
|
46
|
+
Java::JavaxSwing::UIManager.setLookAndFeel(Java::JavaxSwing::UIManager.getSystemLookAndFeelClassName())
|
47
|
+
|
48
|
+
tray = SystemTray.getSystemTray
|
49
|
+
popup = JPopupMenu.new
|
50
|
+
if inside_jar?
|
51
|
+
icon = TrayIcon.new(java.awt.Toolkit::default_toolkit.get_image(java.lang.Object.new.java_class.resource("/gvcs_fx/res/version-control.png")),"G-VCS")
|
52
|
+
else
|
53
|
+
icon = TrayIcon.new(ImageIcon.new(File.join(File.dirname(__FILE__),"..","res","version-control.png")).getImage,"G-VCS")
|
54
|
+
end
|
55
|
+
|
56
|
+
mainAct = JMenuItem.new("G-VCS")
|
57
|
+
mainAct.add_action_listener do |evt|
|
58
|
+
|
59
|
+
#javafx.application.Platform.run_later do
|
60
|
+
#puts "Inside run later!"
|
61
|
+
#begin
|
62
|
+
# app.show_stage
|
63
|
+
#rescue Exception => ex
|
64
|
+
# puts ex
|
65
|
+
# #log_error(ex.message)
|
66
|
+
#end
|
67
|
+
#end
|
68
|
+
#puts "after run_later"
|
69
|
+
end
|
70
|
+
popup.add(mainAct)
|
71
|
+
|
72
|
+
popup.addSeparator
|
73
|
+
|
74
|
+
aboutItm = JMenuItem.new("About")
|
75
|
+
aboutItm.add_action_listener do |evt|
|
76
|
+
end
|
77
|
+
popup.add(aboutItm)
|
78
|
+
|
79
|
+
exitItm = JMenuItem.new("Exit")
|
80
|
+
exitItm.add_action_listener do |evt|
|
81
|
+
ans = JOptionPane::showConfirmDialog(nil,"Exit G-VCS?","Exit Confirmation",JOptionPane::YES_NO_OPTION,JOptionPane::QUESTION_MESSAGE)
|
82
|
+
if ans == JOptionPane::YES_OPTION
|
83
|
+
|
84
|
+
javafx.application.Platform.exit
|
85
|
+
|
86
|
+
SystemTray.getSystemTray.remove(icon)
|
87
|
+
java.lang.System.exit(0)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
popup.add(exitItm)
|
91
|
+
|
92
|
+
# if using this approach the pop up menu will not OS native look...
|
93
|
+
#icon.setPopupMenu(popup)
|
94
|
+
# https://stackoverflow.com/a/17258735
|
95
|
+
icon.add_mouse_listener do |evt|
|
96
|
+
if (evt.getID == java.awt.event.MouseEvent::MOUSE_CLICKED)
|
97
|
+
# isPopupTrigger is recommended if the moust event is mousePressed
|
98
|
+
#if evt.isPopupTrigger
|
99
|
+
popup.setLocation(evt.getX(),evt.getY())
|
100
|
+
popup.setInvoker(popup)
|
101
|
+
popup.visible = true
|
102
|
+
#end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
icon.setImageAutoSize(true)
|
107
|
+
tray.add(icon)
|
108
|
+
|
109
|
+
rescue Exception => ex
|
110
|
+
STDERR.puts ex.backtrace.join("\n")
|
111
|
+
#log_error(ex.message)
|
112
|
+
#fx_alert_error ex.message, "G-VCS Tray Startup Exception"
|
113
|
+
end
|
114
|
+
|
115
|
+
else
|
116
|
+
Global.instance.debug "No tray support"
|
117
|
+
#fx_alert_error("No tray support for this platform")
|
118
|
+
end
|
119
|
+
|
120
|
+
end # show_tray
|
121
|
+
|
122
|
+
def inside_jar?
|
123
|
+
File.dirname(__FILE__)[0..3] == "uri:"
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
end # module Tray
|
128
|
+
end
|
@@ -0,0 +1,547 @@
|
|
1
|
+
# Copyright (C) 2020 Chris Liaw <chrisliaw@antrapol.com>
|
2
|
+
# Author: Chris Liaw <chrisliaw@antrapol.com>
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
|
18
|
+
require_relative '../lib/store'
|
19
|
+
|
20
|
+
java_import javafx.scene.control.cell.PropertyValueFactory
|
21
|
+
java_import javafx.scene.control.TreeTableColumn
|
22
|
+
java_import javafx.collections.FXCollections
|
23
|
+
java_import javafx.beans.property.SimpleStringProperty
|
24
|
+
java_import javafx.scene.control.TreeItem
|
25
|
+
java_import javafx.scene.text.Text
|
26
|
+
java_import javafx.application.Platform
|
27
|
+
|
28
|
+
module GvcsFx
|
29
|
+
module Workspace
|
30
|
+
|
31
|
+
class TreeWorkspace
|
32
|
+
include javafx.beans.value.ObservableValue
|
33
|
+
|
34
|
+
attr_accessor :project, :workspace
|
35
|
+
|
36
|
+
def initialize(proj, ws)
|
37
|
+
@project = proj
|
38
|
+
@workspace = ws
|
39
|
+
end
|
40
|
+
|
41
|
+
def addListener(list)
|
42
|
+
end
|
43
|
+
|
44
|
+
def removeListener(list)
|
45
|
+
end
|
46
|
+
|
47
|
+
def value
|
48
|
+
self
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class WorkspaceCellFactory < javafx.scene.control.TreeTableCell
|
53
|
+
include Antrapol::ToolRack::ExceptionUtils
|
54
|
+
def updateItem(itm,e)
|
55
|
+
super
|
56
|
+
|
57
|
+
cont = nil
|
58
|
+
if not itm.nil?
|
59
|
+
if itm.is_a?(TreeWorkspace)
|
60
|
+
if not_empty?(itm.workspace)
|
61
|
+
cont = itm.workspace.path
|
62
|
+
elsif not_empty?(itm.project)
|
63
|
+
cont = itm.project
|
64
|
+
else
|
65
|
+
cont = itm
|
66
|
+
end
|
67
|
+
else
|
68
|
+
cont = itm
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
setGraphic(Text.new(cont))
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
LEAF_PROJ_DESC = "Leaf Workspaces (Workspace without bind to a project)"
|
79
|
+
ALL_PROJ_FILTER = "All"
|
80
|
+
def init_tblWorkspace
|
81
|
+
|
82
|
+
@trtblWorkspace.columns.clear
|
83
|
+
|
84
|
+
@trtblWorkspace.placeholder = javafx.scene.control.Label.new("Add a workspace to begin")
|
85
|
+
|
86
|
+
@rootNode = TreeItem.new("Managed Workspaces")
|
87
|
+
@rootNode.expanded = true
|
88
|
+
|
89
|
+
# workspace that has not tie to any project
|
90
|
+
@leafProj = TreeItem.new(LEAF_PROJ_DESC)
|
91
|
+
|
92
|
+
@rootNode.children.add(@leafProj)
|
93
|
+
|
94
|
+
col = []
|
95
|
+
tcUrl = TreeTableColumn.new("Path")
|
96
|
+
tcUrl.cell_value_factory = Proc.new do |e|
|
97
|
+
#p e.value.value
|
98
|
+
if e.value.value.is_a?(TreeWorkspace)
|
99
|
+
e.value.value
|
100
|
+
else
|
101
|
+
SimpleStringProperty.new(e.value.value)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
tcUrl.cell_factory = Proc.new do |tc|
|
105
|
+
WorkspaceCellFactory.new
|
106
|
+
end
|
107
|
+
col << tcUrl
|
108
|
+
|
109
|
+
tcLastAcc = TreeTableColumn.new("Last Access")
|
110
|
+
|
111
|
+
tcLastAcc.pref_width = 180.0
|
112
|
+
tcLastAcc.max_width = 180.0
|
113
|
+
tcLastAcc.min_width = 180.0
|
114
|
+
|
115
|
+
col << tcLastAcc
|
116
|
+
|
117
|
+
|
118
|
+
@trtblWorkspace.setRoot(@rootNode)
|
119
|
+
@trtblWorkspace.columns.add_all(col)
|
120
|
+
@trtblWorkspace.setShowRoot(true)
|
121
|
+
|
122
|
+
|
123
|
+
#@trtblWorkspace.columns.clear
|
124
|
+
@trtblWorkspace.column_resize_policy = javafx.scene.control.TreeTableView::CONSTRAINED_RESIZE_POLICY
|
125
|
+
|
126
|
+
#colUrl = TableColumn.new("Path")
|
127
|
+
#colUrl.cell_value_factory = Proc.new do |p|
|
128
|
+
# SimpleStringProperty.new(p.value.path)
|
129
|
+
#end
|
130
|
+
|
131
|
+
#colAccess = TableColumn.new("Last Access")
|
132
|
+
#colAccess.cell_value_factory = Proc.new do |p|
|
133
|
+
# SimpleStringProperty.new(p.value.last_access)
|
134
|
+
#end
|
135
|
+
|
136
|
+
#colAccess.pref_width = 180.0
|
137
|
+
#colAccess.max_width = colAccess.pref_width
|
138
|
+
#colAccess.min_width = colAccess.pref_width
|
139
|
+
|
140
|
+
#@trtblWorkspace.columns.add_all([colUrl, colAccess])
|
141
|
+
|
142
|
+
#@trtblWorkspace.selection_model.selection_mode = javafx.scene.control.SelectionMode::SINGLE
|
143
|
+
|
144
|
+
#@trtblWorkspace.selection_model.selected_items.add_listener do |evt|
|
145
|
+
@trtblWorkspace.selection_model.selected_item_property.add_listener do |obs, oldV, newV|
|
146
|
+
#puts "old value : #{oldV} / new value : #{newV}"
|
147
|
+
if not_empty?(newV)
|
148
|
+
|
149
|
+
if newV.value.is_a?(TreeWorkspace) and not_empty?(newV.value.workspace)
|
150
|
+
|
151
|
+
if File.exist?(newV.value.workspace.path)
|
152
|
+
# Here shall set a global reference to the selected workspace @selWs
|
153
|
+
@selWs = Gvcs::Workspace.new(Global.instance.vcs, newV.value.workspace.path)
|
154
|
+
|
155
|
+
refresh_details
|
156
|
+
show_details
|
157
|
+
else
|
158
|
+
show_landing
|
159
|
+
prompt_error("Workspace '#{newV.value.workspace.path}' does not exist anymore.\nPlease remove it from workspace.")
|
160
|
+
end
|
161
|
+
|
162
|
+
else
|
163
|
+
show_landing
|
164
|
+
end
|
165
|
+
else
|
166
|
+
show_landing
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# project filter
|
171
|
+
@cmbWorkspaceProjectFilter.value_property.add_listener(Proc.new do |ov, oldV, newV|
|
172
|
+
# Javafx combobox not allow changing of internal value while it is updated
|
173
|
+
# if not enclosed in the Platform.run_later()
|
174
|
+
# java.lang.IndexOutOfBoundsException shall be thrown
|
175
|
+
Platform.run_later do
|
176
|
+
|
177
|
+
@projectFilter = newV
|
178
|
+
|
179
|
+
if not_empty?(newV)
|
180
|
+
|
181
|
+
# here shall change the listing of the project
|
182
|
+
refresh_workspace_list
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end)
|
187
|
+
|
188
|
+
refresh_workspace_list
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
def trtblWorkspace_onMouseClicked(evt)
|
193
|
+
if evt.button == javafx.scene.input.MouseButton::SECONDARY
|
194
|
+
# right click on item
|
195
|
+
trtblWorkspace_ctxmnu.show(@trtblWorkspace, evt.screen_x, evt.screen_y)
|
196
|
+
#elsif evt.button == javafx.scene.input.MouseButton::PRIMARY and evt.click_count == 2
|
197
|
+
# # double click on item - diff file
|
198
|
+
# sel = @tblChanges.selection_model.selected_items
|
199
|
+
# if sel.length > 0
|
200
|
+
# sel.each do |s|
|
201
|
+
# st, res = @selWs.diff_file(s.path)
|
202
|
+
# if st
|
203
|
+
|
204
|
+
# show_content_win("Diff Output", "Diff Result - #{s.path}", res)
|
205
|
+
|
206
|
+
# else
|
207
|
+
# set_err_gmsg("Diff for '#{s.path}' failed. [#{res}]")
|
208
|
+
# end
|
209
|
+
|
210
|
+
# end
|
211
|
+
# end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
# hooked to button Add
|
218
|
+
def workspace_add(evt)
|
219
|
+
|
220
|
+
path = @txtWorkspacePath.text
|
221
|
+
if not_empty?(path)
|
222
|
+
|
223
|
+
db = Global.instance.storage
|
224
|
+
|
225
|
+
if not File.exist?(path)
|
226
|
+
prompt_error("Given path '#{path}' does not exist. Please make sure it is a valid path.","Given Path Invalid",main_stage)
|
227
|
+
elsif db.is_workspace_registered?(path)
|
228
|
+
prompt_info("Given path '#{path}' already exist in the list.", "Path Already Exist", main_stage)
|
229
|
+
else
|
230
|
+
|
231
|
+
add_workspace(path)
|
232
|
+
set_success_gmsg("Workspace '#{path}' added to list")
|
233
|
+
|
234
|
+
end # if given path not exist
|
235
|
+
|
236
|
+
else
|
237
|
+
prompt_error "Please provide a folder path for Add operation", "Empty Path", main_stage
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
def trtbleWorkspace_keyreleased(evt)
|
243
|
+
if evt.code == javafx.scene.input.KeyCode::DELETE
|
244
|
+
sel = @trtblWorkspace.selection_model.selected_item
|
245
|
+
remove_workspace(sel)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def trtblWorkspace_dnd_dropped(evt)
|
250
|
+
dnd_dropped(evt)
|
251
|
+
evt.drop_completed = true
|
252
|
+
end
|
253
|
+
|
254
|
+
def trtblWorkspace_dnd_over(evt)
|
255
|
+
evt.acceptTransferModes(javafx.scene.input.TransferMode::LINK)
|
256
|
+
end
|
257
|
+
|
258
|
+
def landing_dnd_dropped(evt)
|
259
|
+
dnd_dropped(evt)
|
260
|
+
evt.drop_completed = true
|
261
|
+
end
|
262
|
+
|
263
|
+
def landing_dnd_over(evt)
|
264
|
+
evt.acceptTransferModes(javafx.scene.input.TransferMode::LINK)
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
def refresh_workspace_list
|
269
|
+
db = Global.instance.storage(true)
|
270
|
+
|
271
|
+
@rootNode.children.clear
|
272
|
+
|
273
|
+
log_debug "#{db.workspaces.length} project(s) found"
|
274
|
+
|
275
|
+
if db.workspaces.length > 0
|
276
|
+
|
277
|
+
db.workspaces.keys.sort.each do |k|
|
278
|
+
|
279
|
+
if not_empty?(@projectFilter) and @projectFilter != ALL_PROJ_FILTER
|
280
|
+
next if k != @projectFilter
|
281
|
+
end
|
282
|
+
|
283
|
+
tws = TreeWorkspace.new(k,nil)
|
284
|
+
pTi = TreeItem.new(tws)
|
285
|
+
db.workspaces[k].each do |ws|
|
286
|
+
ctws = TreeWorkspace.new(k,ws)
|
287
|
+
pTi.children.add(TreeItem.new(ctws))
|
288
|
+
end
|
289
|
+
|
290
|
+
@rootNode.children.add(pTi)
|
291
|
+
end
|
292
|
+
|
293
|
+
@cmbWorkspaceProjectFilter.items.clear
|
294
|
+
pf = [ALL_PROJ_FILTER]
|
295
|
+
pf.concat(db.parent.sort)
|
296
|
+
@cmbWorkspaceProjectFilter.items.add_all(pf)
|
297
|
+
|
298
|
+
# this is so far most acceptable version
|
299
|
+
# if set value will trigger the listener above that shall trigger this method again
|
300
|
+
# become vicious cycle
|
301
|
+
@cmbWorkspaceProjectFilter.prompt_text = @projectFilter if not_empty?(@projectFilter)
|
302
|
+
|
303
|
+
#show_details
|
304
|
+
else
|
305
|
+
show_landing
|
306
|
+
end
|
307
|
+
|
308
|
+
@selWs = nil
|
309
|
+
|
310
|
+
end
|
311
|
+
|
312
|
+
def show_details
|
313
|
+
@pnlLanding.visible = false
|
314
|
+
@tabPnlDetails.visible = true
|
315
|
+
end
|
316
|
+
|
317
|
+
def show_landing
|
318
|
+
@pnlLanding.visible = true
|
319
|
+
@tabPnlDetails.visible = false
|
320
|
+
end
|
321
|
+
|
322
|
+
#def workspace_key_released(evt)
|
323
|
+
# if evt.code == javafx.scene.input.KeyCode::DELETE
|
324
|
+
# sel = @trtblWorkspace.selection_model.selected_items.to_a
|
325
|
+
# if sel.length > 0
|
326
|
+
# s = sel.first.path
|
327
|
+
# res = fx_alert_confirmation("Delete workspace '#{s}' from system?\nThis won't affect physical file.", nil, "Remove Path from Management?", main_stage)
|
328
|
+
# if res == :ok
|
329
|
+
# Global.instance.storage.remove_workspace(s)
|
330
|
+
# Global.instance.storage.store
|
331
|
+
# refresh_workspace_list
|
332
|
+
# refresh_details
|
333
|
+
# log_debug "Workspace '#{s}' removed from management"
|
334
|
+
# set_info_gmsg("Workspace '#{s}' removed from management")
|
335
|
+
# else
|
336
|
+
# set_err_gmsg("Workspace delete operation aborted")
|
337
|
+
# end
|
338
|
+
# end
|
339
|
+
|
340
|
+
# end
|
341
|
+
#end
|
342
|
+
|
343
|
+
def open_workspace(evt)
|
344
|
+
dc = javafx.stage.DirectoryChooser.new
|
345
|
+
dc.title = "Select VCS Workspace"
|
346
|
+
wsDir = dc.showDialog(main_stage)
|
347
|
+
|
348
|
+
@txtWorkspacePath.text = wsDir.absolute_path
|
349
|
+
end
|
350
|
+
|
351
|
+
private
|
352
|
+
def dnd_dropped(evt)
|
353
|
+
f = evt.dragboard.files
|
354
|
+
vcs = Gvcs::Vcs.new
|
355
|
+
ws = []
|
356
|
+
nws = []
|
357
|
+
f.each do |ff|
|
358
|
+
log_debug "Checking drop in path '#{ff.absolute_path}'"
|
359
|
+
st, path = Gvcs::Workspace.new(vcs, ff.absolute_path).workspace_root
|
360
|
+
if st
|
361
|
+
# workspace!
|
362
|
+
ws << path.strip
|
363
|
+
else
|
364
|
+
# not a workspace
|
365
|
+
nws << ff.absolute_path
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
ws.each do |w|
|
370
|
+
log_debug "Adding workspace '#{w}'"
|
371
|
+
add_workspace(w)
|
372
|
+
set_success_gmsg("Workspace '#{w}' added to list")
|
373
|
+
end
|
374
|
+
|
375
|
+
failed = { }
|
376
|
+
nws.each do |w|
|
377
|
+
log_debug "Adding non workspace '#{w}'"
|
378
|
+
st, res = vcs.init(w)
|
379
|
+
if st
|
380
|
+
add_workspace(w)
|
381
|
+
set_success_gmsg("Non-workspace '#{w}' added to list")
|
382
|
+
else
|
383
|
+
failed[w] = res
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
raise_event(Listener::Event[:workspace_added])
|
388
|
+
|
389
|
+
end # dnd_dropped
|
390
|
+
|
391
|
+
def add_workspace(path)
|
392
|
+
|
393
|
+
raise_if_empty?(path, "Path cannot be empty in add workspace operation", GvcsFxException)
|
394
|
+
|
395
|
+
db = Global.instance.storage
|
396
|
+
awStage = javafx.stage.Stage.new
|
397
|
+
dlg = AddWorkspaceController.load_into(awStage)
|
398
|
+
# load existing projects into the combobox
|
399
|
+
if db.parent.length > 0
|
400
|
+
db.parent.each do |pa|
|
401
|
+
dlg.add_option(pa)
|
402
|
+
end
|
403
|
+
else
|
404
|
+
dlg.add_option(LEAF_PROJ_DESC)
|
405
|
+
end
|
406
|
+
|
407
|
+
# set the given path
|
408
|
+
dlg.path = path
|
409
|
+
awStage.title = "Add Workspace"
|
410
|
+
awStage.init_owner(main_stage)
|
411
|
+
|
412
|
+
awStage.showAndWait
|
413
|
+
|
414
|
+
res = dlg.result
|
415
|
+
|
416
|
+
if not_empty?(res)
|
417
|
+
|
418
|
+
projName = nil
|
419
|
+
if not is_empty?(res[0])
|
420
|
+
if res[0] == AddWorkspaceController::ADDPROJ_KEY
|
421
|
+
if is_empty?(res[1])
|
422
|
+
prompt_error("Project name is required. Please try again")
|
423
|
+
else
|
424
|
+
projName = res[1]
|
425
|
+
end
|
426
|
+
else
|
427
|
+
projName = res[0]
|
428
|
+
end
|
429
|
+
|
430
|
+
else
|
431
|
+
projName = LEAF_PROJ_DESC
|
432
|
+
end
|
433
|
+
|
434
|
+
if not_empty?(projName)
|
435
|
+
|
436
|
+
vcs = Gvcs::Vcs.new
|
437
|
+
ds = Global.instance.storage
|
438
|
+
|
439
|
+
tws = Gvcs::Workspace.new(vcs, path)
|
440
|
+
vcs.init(path) if not tws.is_workspace?
|
441
|
+
ds.add_workspace(projName,path)
|
442
|
+
ds.store
|
443
|
+
refresh_workspace_list
|
444
|
+
@txtWorkspacePath.clear
|
445
|
+
|
446
|
+
end
|
447
|
+
|
448
|
+
end
|
449
|
+
|
450
|
+
end # add_workspace
|
451
|
+
|
452
|
+
def remove_workspace(sel)
|
453
|
+
|
454
|
+
if not_empty?(sel)
|
455
|
+
txt = sel.value
|
456
|
+
db = Global.instance.storage
|
457
|
+
|
458
|
+
if txt.is_a?(TreeWorkspace)
|
459
|
+
|
460
|
+
if not_empty?(txt.workspace)
|
461
|
+
|
462
|
+
if db.is_workspace_registered?(txt.workspace.path)
|
463
|
+
# leaf
|
464
|
+
s = txt.workspace.path
|
465
|
+
res = fx_alert_confirmation("Delete workspace '#{s}' from system?\nThis won't affect physical file.", nil, "Remove Path from Management?", main_stage)
|
466
|
+
if res == :ok
|
467
|
+
db.remove_workspace(s)
|
468
|
+
db.store
|
469
|
+
set_success_gmsg("Workspace '#{s}' removed from management")
|
470
|
+
refresh_workspace_list
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
else
|
475
|
+
# project
|
476
|
+
if db.is_project_registered?(txt.project) and db.is_project_empty?(txt.project)
|
477
|
+
res = fx_alert_confirmation("Remove project '#{txt.project}' from the list?", nil, "Confirmation to Remove Project", main_stage)
|
478
|
+
if res == :ok
|
479
|
+
db.delete_project(txt.project)
|
480
|
+
db.store
|
481
|
+
set_success_gmsg("Project '#{txt.project}' removed from management")
|
482
|
+
refresh_workspace_list
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end # txt.is_a?(TreeWorkspace)
|
487
|
+
|
488
|
+
end
|
489
|
+
|
490
|
+
end
|
491
|
+
|
492
|
+
def trtblWorkspace_ctxmnu
|
493
|
+
|
494
|
+
@ctxSel = @trtblWorkspace.selection_model.selected_item
|
495
|
+
trtblWsCtxMenu = javafx.scene.control.ContextMenu.new
|
496
|
+
|
497
|
+
if not_empty?(@ctxSel) and @ctxSel.value.is_a?(TreeWorkspace)
|
498
|
+
|
499
|
+
db = Global.instance.storage
|
500
|
+
tws = @ctxSel.value
|
501
|
+
if not_empty?(tws.workspace)
|
502
|
+
if db.is_workspace_registered?(tws.workspace.path)
|
503
|
+
#
|
504
|
+
# leaf
|
505
|
+
#
|
506
|
+
# delete menu item
|
507
|
+
#
|
508
|
+
delMnuItm = javafx.scene.control.MenuItem.new("Remove from management")
|
509
|
+
delMnuItm.on_action do |evt|
|
510
|
+
|
511
|
+
remove_workspace(@ctxSel)
|
512
|
+
|
513
|
+
end
|
514
|
+
trtblWsCtxMenu.items.add(delMnuItm)
|
515
|
+
#
|
516
|
+
# end diff menu item
|
517
|
+
#
|
518
|
+
end
|
519
|
+
else
|
520
|
+
# project
|
521
|
+
if db.is_project_empty?(tws.project)
|
522
|
+
#
|
523
|
+
# delete menu item
|
524
|
+
#
|
525
|
+
delMnuItm = javafx.scene.control.MenuItem.new("Remove from management")
|
526
|
+
delMnuItm.on_action do |evt|
|
527
|
+
|
528
|
+
remove_workspace(@ctxSel)
|
529
|
+
|
530
|
+
end
|
531
|
+
trtblWsCtxMenu.items.add(delMnuItm)
|
532
|
+
#
|
533
|
+
# end diff menu item
|
534
|
+
#
|
535
|
+
|
536
|
+
end
|
537
|
+
|
538
|
+
end
|
539
|
+
|
540
|
+
end
|
541
|
+
|
542
|
+
|
543
|
+
trtblWsCtxMenu
|
544
|
+
end
|
545
|
+
|
546
|
+
end
|
547
|
+
end
|