$VERSION
=
'0.19'
;
my
$mswin
=
$Config
{
'osname'
} eq
'MSWin32'
;
my
@contextmenu
= (
[
'menu_normal'
,
undef
,
'~Open all files'
,
'git_open_all'
,
'document-open'
],
[
'menu_normal'
,
undef
,
'~Remove from project'
,
'git_remove_dialog'
,
'edit-delete'
],
[
'menu_separator'
,
undef
,
'c1'
],
[
'menu_normal'
,
undef
,
'~Collapse all'
,
'git_collapse'
],
[
'menu_normal'
,
undef
,
'~Expand all'
,
'git_expand'
],
);
sub
new {
my
$class
=
shift
;
my
$self
=
$class
->SUPER::new(
@_
);
return
undef
unless
defined
$self
;
my
$git
= `git -v`;
return
undef
unless
$git
=~/^git\sversion/;
$self
->cmdHookAfter(
'doc_open'
,
'openDocAfter'
,
$self
);
$self
->cmdHookAfter(
'doc_close'
,
'closeDocAfter'
,
$self
);
$self
->cmdHookBefore(
'doc_select'
,
'selectDocBefore'
,
$self
);
$self
->cmdConfig(
git_add
=> [
'gitAdd'
,
$self
],
git_collapse
=> [
'gitCollapse'
,
$self
],
git_command
=> [
'gitCommand'
,
$self
],
git_expand
=> [
'gitExpand'
,
$self
],
git_open_all
=> [
'gitOpenAll'
,
$self
],
git_remove
=> [
'gitRemove'
,
$self
],
git_remove_dialog
=> [
'gitRemoveDialog'
,
$self
],
);
$self
->{PROJECTS} = {};
my
$page
=
$self
->ToolLeftPageAdd(
'Git'
,
'git-icon'
,
undef
,
'Manage your projects'
, 250);
my
$pframe
=
$page
->Frame->
pack
(
-fill
=>
'x'
);
$pframe
->Label(
-text
=>
'Project:'
)->
pack
(
-side
=>
'left'
);
my
$current
=
''
;
$self
->{CURRENT} = \
$current
;
my
$mb
=
$pframe
->Menubutton(
-anchor
=>
'w'
,
-textvariable
=> \
$current
,
)->
pack
(
-side
=>
'left'
,
-expand
=> 1,
-fill
=>
'x'
);
my
$menu
=
$mb
->Menu(
-tearoff
=> 0);
$mb
->configure(
-menu
=>
$menu
);
$self
->{PMENU} =
$menu
;
my
$nav
=
$self
->extGet(
'Selector'
);
my
$gtree
=
$page
->DocumentTree(
-entryselect
=> [
'selectInternal'
,
$self
],
-diriconcall
=> [
'GetDirIcon'
,
$nav
],
-fileiconcall
=> [
'GetFileIcon'
,
$nav
],
-saveiconcall
=> [
'GetSaveIcon'
,
$nav
],
)->
pack
(
-expand
=> 1,
-fill
=>
'both'
);
my
$stack
=
$self
->extGet(
'MenuBar'
)->menuStack(
@contextmenu
);
$gtree
->configure(
'-contextmenu'
,
$stack
);
$self
->{TREE} =
$gtree
;
$self
->
after
(10, [
'doPostConfig'
,
$self
]);
return
$self
;
}
sub
closeDocAfter {
my
(
$self
,
$file
) =
@_
;
if
(
$file
ne 0) {
my
$project
=
$self
->fileInAnyProject(
$file
);
if
(
defined
$project
) {
$self
->projectRemove(
$project
)
unless
$self
->filesOpenInProject(
$project
);
}
}
}
sub
doPostConfig {
my
$self
=
shift
;
my
$mdi
=
$self
->mdi;
my
@list
=
$mdi
->docFullList;
for
(
@list
) {
my
$doc
=
$_
;
my
$folder
=
$self
->projectFind(
$doc
);
if
(
defined
$folder
) {
my
$name
=
$self
->projectName(
$folder
);
$self
->projectAdd(
$name
,
$folder
);
}
}
$self
->
after
(1000, [
'navContextMenu'
,
$mdi
]);
}
sub
fileInAnyProject {
my
(
$self
,
$file
) =
@_
;
my
@list
=
$self
->projectList;
for
(
@list
) {
my
$project
=
$_
;
return
$project
if
$self
->fileInProject(
$project
,
$file
)
}
return
undef
}
sub
fileInProject {
my
(
$self
,
$project
,
$file
) =
@_
;
my
@files
=
$self
->gitFileList(
$project
);
my
$term
=
quotemeta
(
$file
);
my
@containing
=
grep
{ /^
$term
$/ }
@files
;
return
1
if
@containing
;
return
''
}
sub
fileRoot {
my
(
$self
,
$file
) =
@_
;
return
'/'
unless
$mswin
;
if
(
$file
=~ /^([a-zA-Z]\:\\)/) {
return
$1
}
}
sub
filesOpenInProject {
my
(
$self
,
$project
) =
@_
;
my
$mdi
=
$self
->mdi;
my
@g
=
$self
->gitFileList(
$project
);
my
@open
= ();
for
(
@g
) {
push
@open
,
$_
if
$mdi
->docExists(
$_
);
}
return
@open
}
sub
fileStrip {
my
(
$self
,
$project
,
$file
) =
@_
;
my
$offset
=
length
(
$self
->projectFolder(
$project
)) + 1;
$file
=
substr
(
$file
,
$offset
);
return
$file
;
}
sub
gitAdd {
my
$self
=
shift
;
my
$project
=
$self
->projectCurrent;
return
if
$project
eq
''
;
my
$doc
=
$self
->mdi->docSelected;
return
unless
defined
$doc
;
return
unless
-e
$doc
;
return
if
$self
->fileInProject(
$project
,
$doc
);
$doc
=
$self
->fileStrip(
$project
,
$doc
);
$self
->gitCommand(
$project
,
"add $doc"
);
$self
->projectRefresh;
}
sub
gitCollapse {
$_
[0]->{TREE}->collapseAll }
sub
gitCommand {
my
(
$self
,
$project
,
$command
) =
@_
;
my
$folder
=
$self
->projectFolder(
$project
);
return
unless
defined
$folder
;
return
`cd
$folder
; git
$command
`
}
sub
gitExpand {
$_
[0]->{TREE}->expandAll }
sub
gitFileList {
my
(
$self
,
$project
) =
@_
;
my
$list
=
$self
->gitCommand(
$project
,
'ls-files'
);
my
$folder
=
$self
->projectFolder(
$project
);
my
$sep
=
$self
->{TREE}->cget(
'-separator'
);
my
@items
= ();
while
(
$list
=~ s/([^\n]*)\n//) {
my
$file
=
"$folder$sep$1"
;
push
@items
,
$file
}
return
@items
}
sub
gitOpenAll {
my
$self
=
shift
;
my
$project
=
$self
->projectCurrent;
return
if
$project
eq
''
;
my
@list
=
$self
->gitFileList(
$project
);
my
$mdi
=
$self
->mdi;
$mdi
->silentMode(1);
for
(
@list
) {
next
unless
-T
$_
;
$self
->cmdExecute(
'doc_open'
,
$_
)
unless
$mdi
->docExists(
$_
)
}
$mdi
->silentMode(0);
}
sub
gitRemove {
my
(
$self
,
$project
,
$name
) =
@_
;
if
(
$self
->fileInProject(
$project
,
$name
)) {
$self
->cmdExecute(
'doc_close'
,
$name
)
if
$self
->mdi->docExists(
$name
);
$name
=
$self
->fileStrip(
$project
,
$name
);
$self
->gitCommand(
$project
,
"rm -f $name"
);
$self
->projectRefresh;
}
}
sub
gitRemoveDialog {
my
(
$self
,
$project
,
$name
) =
@_
;
$project
=
$self
->projectCurrent
unless
defined
$project
;
return
if
$project
eq
''
;
unless
(
defined
$name
) {
my
$tree
=
$self
->{TREE};
my
$folder
=
$self
->projectFolder(
$project
);
my
$sep
=
$tree
->cget(
'-separator'
);
(
$name
) =
$tree
->infoSelection;
$name
=
"$folder$sep$name"
if
defined
$name
;
}
if
(
defined
$name
) {
my
$answer
=
$self
->popDialog(
'Removing from repository'
,
"Are you sure you want to remove\n$name?"
,
'dialog-warning'
,
'Yes'
,
'No'
,
);
$self
->cmdExecute(
'git_remove'
,
$project
,
$name
)
if
$answer
eq
'Yes'
;
}
}
sub
openDocAfter {
my
$self
=
shift
;
for
(
@_
) {
my
$folder
=
$self
->projectFind(
$_
);
if
(
defined
$folder
) {
my
$name
=
$self
->projectName(
$folder
);
$self
->projectAdd(
$name
,
$folder
);
}
}
}
sub
prj {
return
$_
[0]->{PROJECTS} }
sub
projectAdd {
my
(
$self
,
$project
,
$folder
) =
@_
;
return
if
$project
eq
''
;
return
if
$self
->projectExists(
$project
);
$self
->prj->{
$project
} =
$folder
;
$self
->{PMENU}->add(
'command'
,
-label
=>
$project
,
-command
=> [
'projectSelect'
,
$self
,
$project
],
);
}
sub
projectCurrent {
my
$self
=
shift
;
my
$cur
=
$self
->{CURRENT};
$$cur
=
shift
if
@_
;
return
$$cur
}
sub
projectExists {
my
(
$self
,
$project
) =
@_
;
return
exists
$self
->prj->{
$project
};
}
sub
projectFind {
my
(
$self
,
$file
) =
@_
;
return
undef
unless
-e
$file
;
my
$root
=
$self
->fileRoot(
$file
);
while
(
$file
ne
$root
) {
$file
= dirname(
$file
);
my
$git
=
"$file/.git"
;
return
$file
if
(-e
$git
) and (-d
$git
)
}
return
undef
}
sub
projectFolder {
my
(
$self
,
$project
) =
@_
;
return
$self
->prj->{
$project
};
}
sub
projectList {
my
$self
=
shift
;
my
$prj
=
$self
->prj;
return
sort
keys
%$prj
;
}
sub
projectName {
my
(
$self
,
$folder
) =
@_
;
my
$url
= `cd
$folder
; git config --
local
remote.origin.url`;
if
(
$url
=~ /\/([^\/]+)\.git$/) {
return
$1
}
}
sub
projectRefresh {
my
$self
=
shift
;
my
$tree
=
$self
->{TREE};
$tree
->deleteAll;
my
$cur
=
$self
->projectCurrent;
return
if
$cur
eq
''
;
my
@list
=
$self
->gitFileList(
$cur
);
my
$size
=
@list
;
my
$count
= 0;
$self
->mdi->progressAdd(
'git load'
,
'Loading project'
,
$size
, \
$count
);
for
(
@list
) {
$tree
->entryAdd(
$_
);
$count
++;
$self
->pause(20);
}
$self
->mdi->progressRemove(
'git load'
);
}
sub
projectRemove {
my
(
$self
,
$project
) =
@_
;
return
unless
$self
->projectExists(
$project
);
my
$prj
=
$self
->prj;
my
@p
=
$self
->projectList;
my
$size
=
@p
;
my
$menu
=
$self
->{PMENU};
for
(0 ..
$size
- 1) {
if
(
$menu
->entrycget(
$_
,
'-label'
) eq
$project
) {
$menu
->
delete
(
$_
);
last
;
}
}
$self
->projectSelect(
''
)
if
$self
->projectCurrent eq
$project
;
delete
$prj
->{
$project
};
}
sub
projectSelect {
my
(
$self
,
$project
) =
@_
;
$self
->projectCurrent(
$project
);
$self
->projectRefresh;
}
sub
selectDocBefore {
my
(
$self
,
$file
) =
@_
;
$self
->selectExternal(
$file
);
}
sub
selectExternal {
my
(
$self
,
$name
) =
@_
;
my
$cur
=
$self
->projectCurrent;
return
if
$cur
eq
''
;
$self
->{TREE}->entrySelect(
$name
)
if
$self
->fileInProject(
$cur
,
$name
);
}
sub
selectInternal {
my
(
$self
,
$name
) =
@_
;
my
$mdi
=
$self
->mdi;
unless
(
$mdi
->docExists(
$name
)) {
if
(-T
$name
) {
$self
->cmdExecute(
'doc_open'
,
$name
);
}
else
{
$self
->openURL(
$name
)
}
}
$self
->cmdExecute(
'doc_select'
,
$name
)
if
$mdi
->docExists(
$name
);
}
sub
Unload {
my
$self
=
shift
;
$self
->ToolLeftPageRemove(
'Git'
);
for
(
'git_add'
,
'git_collapse'
,
'git_command'
,
'git_expand'
,
'git_open_all'
,
'git_remove'
,
'git_remove_dialog'
,
) {
$self
->cmdRemove(
$_
);
}
$self
->cmdUnhookAfter(
'doc_close'
,
'closeDocAfter'
,
$self
);
$self
->cmdUnhookAfter(
'doc_open'
,
'openDocAfter'
,
$self
);
$self
->cmdUnhookBefore(
'doc_select'
,
'selectDocBefore'
,
$self
);
my
$flag
=
$self
->SUPER::Unload;
my
$mdi
=
$self
->mdi;
$self
->
after
(100, [
'navContextMenu'
,
$mdi
]);
return
$flag
;
}
1;