├── .gitignore ├── .projectile ├── Changes ├── README.md ├── bin └── sqitch-gui ├── dist.ini ├── images ├── sqitch-gui-1.png ├── sqitch-gui-2.png └── sqitch-gui-3.png ├── lib └── App │ └── Sqitch │ ├── GUI.pm │ └── GUI │ ├── Config.pm │ ├── Config │ └── Toolbar.pm │ ├── Controller.pm │ ├── Model.pm │ ├── Model │ ├── Cell.pm │ ├── Grid.pm │ ├── ListDataTable.pm │ ├── PlanItem.pm │ └── ProjectItem.pm │ ├── Options.pm │ ├── Refresh.pm │ ├── Roles │ ├── Element.pm │ ├── Observable.pm │ ├── Observer.pm │ └── Panel.pm │ ├── Rules.pm │ ├── Sqitch.pm │ ├── Status.pm │ ├── Target.pm │ ├── Types.pm │ ├── View.pm │ ├── View │ ├── Dialog │ │ ├── About.pm │ │ ├── Help.pm │ │ ├── Projects.pm │ │ ├── Refresh.pm │ │ ├── Rules.pm │ │ └── Status.pm │ └── Panel │ │ ├── Bottom.pm │ │ ├── Change.pm │ │ ├── Left.pm │ │ ├── Plan.pm │ │ ├── Project.pm │ │ ├── Right.pm │ │ └── Top.pm │ ├── Wx │ ├── Editor.pm │ ├── Listctrl.pm │ ├── LogView.pm │ ├── Menubar.pm │ ├── Notebook.pm │ ├── Statusbar.pm │ └── Toolbar.pm │ └── WxApp.pm ├── po ├── App-Sqitch-GUI.pot └── ro.po ├── share └── etc │ └── icons │ ├── README │ ├── preferences-desktop.png │ ├── preferences-system.png │ └── system-log-out.png ├── t ├── 00-load.t ├── config.t ├── home │ ├── .sqitch │ │ └── sqitch.conf │ ├── flipr │ │ ├── deploy │ │ │ └── users.sql │ │ ├── revert │ │ │ └── users.sql │ │ ├── sqitch.conf │ │ ├── sqitch.plan │ │ └── verify │ │ │ └── users.sql │ └── intro │ │ ├── README.md │ │ ├── deploy │ │ ├── appschema.sql │ │ ├── change_pass.sql │ │ ├── change_pass@v1.0.0-dev2.sql │ │ ├── delete_flip.sql │ │ ├── delete_list.sql │ │ ├── flips.sql │ │ ├── insert_flip.sql │ │ ├── insert_list.sql │ │ ├── insert_user.sql │ │ ├── insert_user@v1.0.0-dev2.sql │ │ ├── lists.sql │ │ ├── pgcrypto.sql │ │ └── users.sql │ │ ├── revert │ │ ├── appschema.sql │ │ ├── change_pass.sql │ │ ├── change_pass@v1.0.0-dev2.sql │ │ ├── delete_flip.sql │ │ ├── delete_list.sql │ │ ├── flips.sql │ │ ├── insert_flip.sql │ │ ├── insert_list.sql │ │ ├── insert_user.sql │ │ ├── insert_user@v1.0.0-dev2.sql │ │ ├── lists.sql │ │ ├── pgcrypto.sql │ │ └── users.sql │ │ ├── sqitch.conf │ │ ├── sqitch.plan │ │ └── verify │ │ ├── appschema.sql │ │ ├── change_pass.sql │ │ ├── change_pass@v1.0.0-dev2.sql │ │ ├── delete_flip.sql │ │ ├── delete_list.sql │ │ ├── flips.sql │ │ ├── insert_flip.sql │ │ ├── insert_list.sql │ │ ├── insert_user.sql │ │ ├── insert_user@v1.0.0-dev2.sql │ │ ├── lists.sql │ │ ├── pgcrypto.sql │ │ └── users.sql ├── model-datatable.t ├── model-projectitem.t ├── model.t ├── observer.t ├── planitem.t ├── status.t ├── target.t ├── timezone.t ├── wx-listctrl.t └── wx-toolbar.t └── xt ├── manifest.t ├── pod-coverage.t └── pod.t /.gitignore: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are considered comments. 2 | *.log 3 | *.old 4 | *.tmp 5 | *~ 6 | .#* 7 | .al 8 | .build/ 9 | .last_cover_stats 10 | Build 11 | Build.bat 12 | MANIFEST 13 | MANIFEST.bak 14 | META.yml 15 | MYMETA.* 16 | Makefile 17 | Makefile.old 18 | TAGS 19 | \#*# 20 | _build/ 21 | blib/ 22 | cover_db/ 23 | nytprof.out 24 | pm_to_blib 25 | # Cava 26 | Cava/Linux/cava20-logs/ 27 | Cava/Linux/cava20-temp/ 28 | Cava/Linux/installer/ 29 | Cava/Linux/release/ 30 | *.db 31 | TAGS 32 | *.mo 33 | .perl-version 34 | -------------------------------------------------------------------------------- /.projectile: -------------------------------------------------------------------------------- 1 | # Ignore 2 | -/.build 3 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | Revision history for App-Sqitch-GUI 2 | 3 | 0.01 Date/time 4 | First version, released on an unsuspecting world. 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | App::Sqitch::GUI version 0.020 2 | ============================== 3 | Ștefan Suciu 4 | 2016-08-17 5 | 6 | GUI for Sqitch - Simple SQL change management. 7 | 8 | Please see [Sqitch](http://sqitch.org) for a detailed description of the 9 | command line application. 10 | 11 | 12 | Requirements 13 | ------------ 14 | 15 | - Perl v5.10.01 or newer; 16 | - Sqitch v0.9996 or newer; 17 | - wxPerl, Moo and other modules; 18 | 19 | Note: Moose is not a requirement starting with v0.020. 20 | 21 | 22 | Status 23 | ------ 24 | 25 | Draft - Limited functionality: 26 | 27 | - Add project; 28 | - Set a default project; 29 | - Load a project and view it's details; 30 | 31 | 32 | Test usage 33 | ---------- 34 | 35 | % git clone https://github.com/stefansbv/sqitch-gui.git 36 | % cd sqitch-gui 37 | 38 | # Install prereqs 39 | % dzil authordeps --missing | cpanm 40 | % dzil listdeps --missing | cpanm 41 | 42 | % PERL5LIB=lib perl bin/sqitch-gui 43 | 44 | After the application start, if there is no project configured yet, a 45 | dialog is automaticaly opened. Click on the `Add` button and select a 46 | path to an existing Sqitch project. The path should be updated in the 47 | list. Click on the close button. 48 | 49 | Select a project and Press the `Load` button. 50 | 51 | Still much work to do but some feedback, in this stage, would be 52 | awesome... 53 | 54 | Screenshots 55 | ----------- 56 | 57 | The Project Panel 58 | 59 | ![The Project Panel](images/sqitch-gui-1.png) 60 | 61 | The Plan Panel 62 | 63 | ![The Plan Panel](images/sqitch-gui-2.png) 64 | 65 | The Change Panel 66 | 67 | ![The Change Panel](images/sqitch-gui-3.png) 68 | 69 | Implementation notes 70 | -------------------- 71 | 72 | Sqitch is a command line program. The user runs `sqitch`, with the 73 | appropriate parameters each time he wants to execute a command. This 74 | is very different from a graphical user interface. A program with a 75 | GUI once started expects and executes the commands from the user until 76 | it is closed. 77 | 78 | To integrate Sqitch into Sqitch::GUI there are a few problems that 79 | have to be solved: 80 | 81 | The `sqitch` command is executed from a project directory from 82 | where it reads some configurations and the plan file. To be useful 83 | Sqitch::GUI needs to keep the list of repositories in a configuration 84 | file located in standard places in order to be able to find, read and 85 | update it. 86 | 87 | Command line parameter options, technically, can be used for GUI 88 | executable but is better to use only configurations instead. 89 | 90 | Each command from the Sqitch interface should have a dedicated button 91 | in the GUI, but some of the commands take options (sometimes required 92 | ones, like `sqitch show`). 93 | 94 | User Interaction - some commands require confirmation from the user. 95 | This can be implemented by using custom dialogs for each command. 96 | 97 | 98 | License And Copyright 99 | --------------------- 100 | 101 | Copyright (C) 2012-2016 iovation Inc. 102 | 103 | Copyright (C) 2016 Ștefan Suciu. 104 | 105 | The license is the same as for Sqitch: 106 | 107 | Permission is hereby granted, free of charge, to any person obtaining 108 | a copy of this software and associated documentation files (the 109 | "Software"), to deal in the Software without restriction, including 110 | without limitation the rights to use, copy, modify, merge, publish, 111 | distribute, sublicense, and/or sell copies of the Software, and to 112 | permit persons to whom the Software is furnished to do so, subject to 113 | the following conditions: 114 | 115 | The above copyright notice and this permission notice shall be 116 | included in all copies or substantial portions of the Software. 117 | 118 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 119 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 120 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 121 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 122 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 123 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 124 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 125 | -------------------------------------------------------------------------------- /bin/sqitch-gui: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # ABSTRACT: GUI for Sqitch 4 | # PODNAME: sqitch-gui 5 | 6 | use strict; 7 | use warnings; 8 | 9 | use App::Sqitch::GUI; 10 | 11 | my $app = App::Sqitch::GUI->new; 12 | 13 | $app->run; 14 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = App-Sqitch-GUI 2 | license = MIT 3 | copyright_holder = Ștefan Suciu 4 | copyright_year = 2016 5 | version = 0.020 6 | main_module = lib/App/Sqitch/GUI.pm 7 | 8 | [MetaResources] 9 | homepage = http://sqitch.org/ 10 | repository.url = http://github.com/stefansbv/sqitch-gui/ 11 | bugtracker.web = http://github.com/stefansbv/sqitch-gui/issues/ 12 | repository.type = git 13 | 14 | [GatherDir] 15 | include_dotfiles = 1 16 | [PruneCruft] 17 | [ManifestSkip] 18 | [MetaYAML] 19 | [License] 20 | [Readme] 21 | [ExtraTests] 22 | [ExecDir] 23 | [ShareDir] 24 | [MakeMaker] 25 | [Manifest] 26 | [TestRelease] 27 | [ConfirmRelease] 28 | [UploadToCPAN] 29 | [MetaJSON] 30 | [PkgVersion] 31 | [PodWeaver] 32 | 33 | [AutoPrereqs] 34 | skip = ^Win32 35 | skip = ^DBD:: 36 | skip = ^Wx:: 37 | 38 | [Prereqs] 39 | Wx::Scintilla = 0 40 | 41 | [LocaleTextDomain] 42 | share_dir = share 43 | -------------------------------------------------------------------------------- /images/sqitch-gui-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqitchers/sqitch-gui/31905dd0f21bd8d7a69c18d3af060d605ce81160/images/sqitch-gui-1.png -------------------------------------------------------------------------------- /images/sqitch-gui-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqitchers/sqitch-gui/31905dd0f21bd8d7a69c18d3af060d605ce81160/images/sqitch-gui-2.png -------------------------------------------------------------------------------- /images/sqitch-gui-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqitchers/sqitch-gui/31905dd0f21bd8d7a69c18d3af060d605ce81160/images/sqitch-gui-3.png -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI; 2 | 3 | # ABSTRACT: GUI for Sqitch 4 | 5 | use 5.010; 6 | use Moo; 7 | use App::Sqitch::GUI::Types qw( 8 | SqitchGUIController 9 | ); 10 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 11 | use Locale::Messages qw(bind_textdomain_filter); 12 | use App::Sqitch::GUI::Controller; 13 | 14 | BEGIN { 15 | # Force Locale::TextDomain to encode in UTF-8 and to decode all messages. 16 | $ENV{OUTPUT_CHARSET} = 'UTF-8'; 17 | bind_textdomain_filter 'App-Sqitch-GUI' => \&Encode::decode_utf8; 18 | } 19 | 20 | has 'controller' => ( 21 | is => 'rw', 22 | isa => SqitchGUIController, 23 | lazy => 1, 24 | builder => '_build_controller', 25 | ); 26 | 27 | sub _build_controller { 28 | return App::Sqitch::GUI::Controller->new(); 29 | } 30 | 31 | sub run { 32 | shift->controller->app->MainLoop; 33 | } 34 | 35 | 1; 36 | 37 | =head1 AUTHOR 38 | 39 | Stefan Suciu, C<< >> 40 | 41 | =head1 BUGS 42 | 43 | None known. 44 | 45 | Please report any bugs or feature requests to the author. 46 | 47 | =head1 ACKNOWLEDGMENTS 48 | 49 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 50 | project: 51 | 52 | https://github.com/tmtowtdi/LacunaWaX 53 | 54 | Copyright: Jonathan D. Barton 2012-2013 55 | 56 | The implementation of the localization code is based on the work of 57 | David E. Wheeler. 58 | 59 | Thank you! 60 | 61 | =head1 LICENSE AND COPYRIGHT 62 | 63 | Stefan Suciu 2013 64 | 65 | This program is free software; you can redistribute it and/or modify it 66 | under the terms of either: the GNU General Public License as published 67 | by the Free Software Foundation. 68 | 69 | =cut 70 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Config.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Config; 2 | 3 | # ABSTRACT: A Sqitch::Config Extension 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use MooX::HandlesVia; 9 | use App::Sqitch::GUI::Types qw( 10 | Dir 11 | Str 12 | Maybe 13 | HashRef 14 | ); 15 | use Path::Class qw(dir file); 16 | use File::ShareDir qw(dist_dir); 17 | use Try::Tiny; 18 | use List::Util qw(first); 19 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 20 | use App::Sqitch::X qw(hurl); 21 | 22 | extends 'App::Sqitch::Config'; 23 | 24 | has 'default_project_name' => ( 25 | is => 'rw', 26 | isa => Maybe[Str], 27 | lazy => 1, 28 | default => sub { 29 | my $self = shift; 30 | return $self->get(key => 'core.project'); 31 | } 32 | ); 33 | 34 | has 'default_project_path' => ( 35 | is => 'rw', 36 | isa => Maybe[Dir], 37 | lazy => 1, 38 | default => sub { 39 | my $self = shift; 40 | my $default = $self->default_project_name; 41 | return $default 42 | ? dir $self->get( key => "project.${default}.path" ) 43 | : undef; 44 | } 45 | ); 46 | 47 | has 'current_project_path' => ( 48 | is => 'rw', 49 | isa => Dir, 50 | required => 0, 51 | ); 52 | 53 | sub local_file { 54 | my $self = shift; 55 | return $self->current_project_path 56 | ? file( $self->current_project_path, $self->confname ) 57 | : undef; 58 | }; 59 | 60 | has '_conf_projects_list' => ( 61 | is => 'ro', 62 | isa => Maybe[HashRef], 63 | lazy => 1, 64 | default => sub { 65 | shift->get_regexp( key => qr/^project[.][^.]+[.]path$/ ); 66 | } 67 | ); 68 | 69 | has 'project_list' => ( 70 | is => 'ro', 71 | handles_via => 'Hash', 72 | lazy => 1, 73 | builder => '_build_project_list', 74 | handles => { 75 | get_project => 'get', 76 | has_project => 'count', 77 | projects => 'kv', 78 | }, 79 | ); 80 | 81 | sub _build_project_list { 82 | my $self = shift; 83 | my $project_list = {}; 84 | while ( my ( $key, $path ) = each( %{ $self->_conf_projects_list } ) ) { 85 | my ($name) = $key =~ m{^project[.](.+)[.]path$}; 86 | $project_list->{$name} = dir $path; 87 | } 88 | return $project_list; 89 | } 90 | 91 | has 'engine_list' => ( 92 | handles_via => 'Hash', 93 | is => 'ro', 94 | required => 1, 95 | lazy => 1, 96 | default => sub { 97 | { unknown => 'Unknown', 98 | pg => 'PostgreSQL', 99 | mysql => 'MySQL', 100 | sqlite => 'SQLite', 101 | oracle => 'Oracle', 102 | firebird => 'Firebird', 103 | }; 104 | }, 105 | handles => { 106 | get_engine_name => 'get', 107 | }, 108 | ); 109 | 110 | sub get_engine_from_name { 111 | my ($self, $engine) = @_; 112 | my %engines = reverse %{ $self->engine_list }; 113 | return $engines{$engine}; 114 | } 115 | 116 | #-- Not used, yet: 117 | 118 | # sub has_repo_name { 119 | # my ($self, $name) = @_; 120 | # hurl 'Wrong arguments passed to has_repo_name()' 121 | # unless $name; 122 | # return 1 if first { $name eq $_ } keys %{$self->project_list}; 123 | # return 0; 124 | # } 125 | 126 | # sub has_repo_path { 127 | # my ($self, $path) = @_; 128 | # hurl 'Wrong arguments passed to has_repo_path()' unless $path; 129 | # return 1 if first { $path->stringify eq $_ } values %{$self->project_list}; 130 | # return 0; 131 | # } 132 | 133 | sub reload { 134 | my ( $self, $path ) = @_; 135 | hurl 'Wrong arguments passed to reload()' unless $path and -d $path; 136 | my $file = file $path, $self->confname; 137 | hurl reload => 138 | __x( 'The configuration file "{file}" does not exist!', file => $file ) 139 | unless $file and -f $file; 140 | try { $self->load_file($file) } 141 | catch { 142 | hurl reload => __x( 143 | 'The configuration file "{file}" could not be (re)loaded: {err}', 144 | file => $file, 145 | err => $_, 146 | ); 147 | }; 148 | $self->current_project_path( dir $path); 149 | return; 150 | } 151 | 152 | sub project_list_cnt { 153 | my $self = shift; 154 | return scalar keys %{ $self->project_list }; 155 | } 156 | 157 | has 'sharedir' => ( 158 | is => 'ro', 159 | isa => Dir, 160 | lazy => 1, 161 | default => sub { 162 | my $self = shift; 163 | my $path; 164 | try { 165 | $path = dist_dir('App-Sqitch-GUI'); 166 | } 167 | catch { 168 | $path = 'share'; 169 | }; 170 | return dir $path; 171 | }, 172 | ); 173 | 174 | has 'icon_path' => ( 175 | is => 'ro', 176 | isa => Dir, 177 | default => sub { 178 | my $self = shift; 179 | return dir( $self->sharedir, 'etc', 'icons' ); 180 | }, 181 | ); 182 | 183 | =pod 184 | 185 | Additions to the user configuration file C: 186 | 187 | [core] 188 | engine = pg 189 | project = flipr 190 | [project "flipr"] 191 | path = /home/user/sqitch/flipr 192 | [project "widgets"] 193 | path = /home/user/sqitch/widgets 194 | 195 | The list of project names and paths and a default project name. 196 | 197 | =cut 198 | 199 | 1; 200 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Config/Toolbar.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Config::Toolbar; 2 | 3 | # ABSTRACT: Data store for Toolbar 4 | 5 | use Moo; 6 | use MooX::HandlesVia; 7 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 8 | 9 | has 'toolnames' => ( 10 | is => 'ro', 11 | handles_via => 'Array', 12 | default => sub { 13 | return [ 'tb_pj', 'tb_qt', ]; 14 | }, 15 | handles => { all_buttons => 'elements', }, 16 | ); 17 | 18 | has 'tool' => ( 19 | is => 'rw', 20 | handles_via => 'Hash', 21 | default => sub { 22 | return { 23 | tb_pj => { 24 | tooltip => __ 'Projects', 25 | icon => 'preferences-system.png', 26 | sep => 'after', 27 | help => __ 'Projects', 28 | type => '_item_normal', 29 | id => '1001', 30 | state => { 31 | init => 'normal', 32 | idle => 'normal', 33 | } 34 | }, 35 | tb_qt => { 36 | tooltip => __ 'Quit', 37 | icon => 'system-log-out.png', 38 | sep => 'none', 39 | help => __ 'Quit the application', 40 | type => '_item_normal', 41 | id => '1002', 42 | state => { 43 | init => 'normal', 44 | idle => 'normal', 45 | } 46 | }, 47 | }; 48 | }, 49 | handles => { 50 | ids_in_tool => 'keys', 51 | get_tool => 'get', 52 | } 53 | ); 54 | 55 | 1; 56 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Model.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Model; 2 | 3 | # ABSTRACT: The Model 4 | 5 | use 5.010; 6 | use Moo; 7 | use MooX::HandlesVia; 8 | use App::Sqitch::GUI::Types qw( 9 | SqitchGUIConfig 10 | SqitchGUIModelListDataTable 11 | SqitchGUIModelPlanItem 12 | SqitchGUIModelProjectItem 13 | ); 14 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 15 | use App::Sqitch::X qw(hurl); 16 | use Try::Tiny; 17 | use Path::Class; 18 | 19 | use App::Sqitch::GUI::Sqitch; 20 | use App::Sqitch::GUI::Target; 21 | use App::Sqitch::GUI::Model::ListDataTable; 22 | use App::Sqitch::GUI::Model::ProjectItem; 23 | use App::Sqitch::GUI::Model::PlanItem; 24 | 25 | has 'config' => ( 26 | is => 'ro', 27 | isa => SqitchGUIConfig, 28 | lazy => 1, 29 | ); 30 | 31 | has 'current_project' => ( 32 | is => 'rw', 33 | isa => SqitchGUIModelProjectItem, 34 | lazy => 1, 35 | builder => '_build_current_project', 36 | ); 37 | 38 | sub _build_current_project { 39 | my $self = shift; 40 | 41 | # At build time, the current item is the default item 42 | my $item = App::Sqitch::GUI::Model::ProjectItem->new; 43 | if ( my $name = $self->config->default_project_name ) { 44 | if ( my $path = $self->config->default_project_path ) { 45 | $item->name($name); 46 | $item->path($path); 47 | } 48 | } 49 | return $item; 50 | } 51 | 52 | has 'current_plan_item' => ( 53 | is => 'rw', 54 | isa => SqitchGUIModelPlanItem, 55 | lazy => 1, 56 | builder => '_build_current_plan_item', 57 | ); 58 | 59 | sub _build_current_plan_item { 60 | my $self = shift; 61 | my $item = App::Sqitch::GUI::Model::PlanItem->new; 62 | return $item; 63 | } 64 | 65 | has 'project_config_issues' => ( 66 | is => 'rw', 67 | handles_via => 'Array', 68 | lazy => 1, 69 | handles => { 70 | config_has_issues => 'count', 71 | config_add_issue => 'push', 72 | config_all_issues => 'elements', 73 | }, 74 | default => sub { [] }, 75 | ); 76 | 77 | has 'default_project' => ( 78 | is => 'rw', 79 | isa => SqitchGUIModelProjectItem, 80 | lazy => 1, 81 | builder => '_build_default_project', 82 | ); 83 | 84 | sub _build_default_project { 85 | my $self = shift; 86 | my $item = App::Sqitch::GUI::Model::ProjectItem->new; 87 | if ( my $name = $self->config->default_project_name ) { 88 | if ( my $path = $self->config->default_project_path ) { 89 | $item->name($name); 90 | $item->path($path); 91 | } 92 | else { 93 | $self->config_add_issue( 94 | __x 'EE The "{name}" project has no associated path and is set as default', 95 | name => $name ); 96 | } 97 | } 98 | return $item; 99 | } 100 | 101 | has project_list => ( 102 | is => 'ro', 103 | handles_via => 'Hash', 104 | lazy => 1, 105 | builder => '_build_project_list', 106 | handles => { 107 | get_project => 'get', 108 | has_project => 'count', 109 | projects => 'kv', 110 | }, 111 | ); 112 | 113 | sub _build_project_list { 114 | my $self = shift; 115 | my $projects = {}; 116 | my ( %seen_name, %seen_path ); 117 | for my $rec ( $self->config->projects ) { 118 | my ( $name, $path ) = @{$rec}; 119 | 120 | $seen_name{$name}++; 121 | $self->config_add_issue( __x 'EE Duplicate name found: "{name}"', 122 | name => $name ) 123 | if defined $seen_name{$name} and $seen_name{$name} > 1; 124 | $seen_path{$path}++; 125 | $self->config_add_issue( __x 'EE Duplicate path found: "{path}"', 126 | path => $path ) 127 | if defined $seen_path{$path} and $seen_path{$path} > 1; 128 | unless ( $self->is_project_path($path) ) { 129 | $self->config_add_issue( 130 | __x 'WW The "{path}" path does not look like a Sqitch project!', 131 | path => $path 132 | ); 133 | } 134 | 135 | my $engine = $self->get_project_engine_from_name($name); 136 | my $default = $self->default_project->name // q{}; 137 | my $current = $self->current_project->name // q{}; 138 | my $is_default = $name eq $default ? 1 : 0; 139 | my $is_current = $name eq $current ? 1 : 0; 140 | $projects->{$name} = { 141 | name => $name, 142 | path => $path, 143 | engine => $engine, 144 | default => $is_default, 145 | current => $is_current, 146 | }; 147 | } 148 | return $projects; 149 | } 150 | 151 | sub is_project_path { 152 | my ($self, $path) = @_; 153 | my $cfg_file = file $path, $self->config->confname; 154 | return 0 unless -f $cfg_file->stringify; 155 | return 1; 156 | } 157 | 158 | sub is_empty_path { 159 | my ($self, $path) = @_; 160 | my $dir = dir $path; 161 | my $is_empty = $dir->stat && !$dir->children; # thanks tobyink from PM ;) 162 | return $is_empty; 163 | } 164 | 165 | sub get_project_engine_from_name { 166 | my ($self, $name) = @_; 167 | my $path = $self->config->get_project($name); 168 | return $self->get_project_engine_from_path($path); 169 | } 170 | 171 | sub get_project_engine_from_path { 172 | my ($self, $path) = @_; 173 | my $cfg_file = file $path, $self->config->confname; 174 | if ( -f $cfg_file ) { 175 | my $cfg_href = Config::GitLike->load_file($cfg_file); 176 | if ( exists $cfg_href->{'core.engine'} ) { 177 | my $engine_code = $cfg_href->{'core.engine'}; 178 | my $engine_name = $self->config->get_engine_name($engine_code); 179 | return $engine_name; 180 | } 181 | } 182 | return; 183 | } 184 | 185 | #-- List data 186 | 187 | has 'project_list_data' => ( 188 | is => 'ro', 189 | isa => SqitchGUIModelListDataTable, 190 | default => sub { 191 | return App::Sqitch::GUI::Model::ListDataTable->new; 192 | }, 193 | ); 194 | 195 | has 'plan_list_data' => ( 196 | is => 'ro', 197 | isa => SqitchGUIModelListDataTable, 198 | default => sub { 199 | return App::Sqitch::GUI::Model::ListDataTable->new; 200 | }, 201 | ); 202 | 203 | #-- Lists meta data 204 | 205 | sub project_dlg_list_meta_data { 206 | return [ 207 | { field => 'recno', 208 | label => '#', 209 | align => 'center', 210 | width => 25, 211 | type => 'int', 212 | }, 213 | { field => 'name', 214 | label => __ 'Name', 215 | align => 'left', 216 | width => 100, 217 | type => 'str', 218 | }, 219 | { field => 'path', 220 | label => __ 'Path', 221 | align => 'left', 222 | width => 327, 223 | type => 'str', 224 | }, 225 | ]; 226 | } 227 | 228 | sub project_list_meta_data { 229 | return [ 230 | { field => 'recno', 231 | label => '#', 232 | align => 'center', 233 | width => 25, 234 | type => 'int', 235 | }, 236 | { field => 'name', 237 | label => __ 'Name', 238 | align => 'left', 239 | width => 160, 240 | type => 'str', 241 | }, 242 | { field => 'path', 243 | label => __ 'Path', 244 | align => 'left', 245 | width => 415, 246 | type => 'str', 247 | }, 248 | { field => 'engine', 249 | label => __ 'Engine', 250 | align => 'left', 251 | width => 100, 252 | type => 'str', 253 | }, 254 | { field => 'default', 255 | label => __ 'Default', 256 | align => 'center', 257 | width => 70, 258 | type => 'str', 259 | }, 260 | { field => 'current', 261 | label => __ 'Current', 262 | align => 'center', 263 | width => 70, 264 | type => 'str', 265 | }, 266 | ]; 267 | } 268 | 269 | sub plan_list_meta_data { 270 | return [ 271 | { field => 'recno', 272 | label => '#', 273 | align => 'center', 274 | width => 35, 275 | type => 'int', 276 | }, 277 | { field => 'name', 278 | label => __ 'Name', 279 | align => 'left', 280 | width => 130, 281 | type => 'str', 282 | }, 283 | { field => 'create_time', 284 | label => __ 'Create time', 285 | align => 'left', 286 | width => 160, 287 | type => 'str', 288 | }, 289 | { field => 'creator', 290 | label => __ 'Creator', 291 | align => 'center', 292 | width => 110, 293 | type => 'str', 294 | }, 295 | { field => 'description', 296 | label => __ 'Description', 297 | align => 'left', 298 | width => 260, 299 | type => 'str', 300 | }, 301 | { field => 'current', 302 | label => __ 'Current', 303 | align => 'center', 304 | width => 60, 305 | type => 'str', 306 | }, 307 | { field => 'status', 308 | label => __ 'Status', 309 | align => 'center', 310 | width => 60, 311 | type => 'str', 312 | }, 313 | ]; 314 | } 315 | 316 | 1; 317 | 318 | __END__ 319 | 320 | =encoding utf8 321 | 322 | =head1 DESCRIPTION 323 | 324 | The Model. 325 | 326 | =head1 SYNOPSIS 327 | 328 | =head1 ATTRIBUTES 329 | 330 | =head2 C 331 | 332 | =head2 C 333 | 334 | =head2 C 335 | 336 | =head2 C 337 | 338 | =head2 C 339 | 340 | =head2 C 341 | 342 | =head2 C 343 | 344 | =head2 C 345 | 346 | =head2 C 347 | 348 | =head1 METHODS 349 | 350 | =head2 C 351 | 352 | Return true if the path contains a Sqitch project. 353 | 354 | XXX: Is enough to check if it has a sqitch.conf file in it? 355 | 356 | =head2 C 357 | 358 | =head2 C 359 | 360 | =head2 C 361 | 362 | =head2 C 363 | 364 | =head2 C 365 | 366 | =head2 C 367 | 368 | =cut 369 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Model/Cell.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Model::Cell; 2 | 3 | # ABSTRACT: Data Table Cell 4 | 5 | use Moo; 6 | use Types::Standard -types; 7 | 8 | has name => ( 9 | is => 'rw', 10 | isa => Str, 11 | ); 12 | 13 | 1; 14 | 15 | =head1 ACKNOWLEDGMENTS 16 | 17 | Code copied and adapted from http://www.perlmonks.org/?node_id=1052124 18 | 19 | Thanks tobyink. 20 | 21 | =cut 22 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Model/Grid.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Model::Grid; 2 | 3 | # ABSTRACT: Data Table Grid 4 | 5 | use Moo; 6 | use Types::Standard -types; 7 | use List::Objects::Types -types; 8 | 9 | use App::Sqitch::X qw(hurl); 10 | use App::Sqitch::GUI::Model::Cell; 11 | 12 | my $CellType 13 | = ( InstanceOf ['App::Sqitch::GUI::Model::Cell'] ) 14 | ->plus_coercions( 15 | Str, sub { App::Sqitch::GUI::Model::Cell->new( name => $_ ) }, 16 | ); 17 | 18 | has cells => ( 19 | is => 'rw', 20 | isa => TypedArray [ TypedArray [$CellType] ], 21 | coerce => 1, 22 | handles => { 23 | get_row => 'get', 24 | set_row => 'set', 25 | all_rows => 'all', 26 | add_row => 'push', 27 | remove_row => 'splice', 28 | rows_no => 'count', 29 | }, 30 | ); 31 | 32 | sub get_cell { 33 | my $self = shift; 34 | my ( $row, $col ) = @_; 35 | if ( my $item = $self->get_row($row) ) { 36 | return $item->get($col); 37 | } 38 | else { 39 | hurl "Error, no such row: $row"; 40 | } 41 | } 42 | 43 | sub set_cell { 44 | my $self = shift; 45 | my ($row, $col, $value) = @_; 46 | if ( my $item = $self->get_row($row) ) { 47 | $item->set($col, $value); 48 | } 49 | else { 50 | hurl "Error, no such row: $row"; 51 | } 52 | return 1; 53 | } 54 | 55 | sub all_cells { 56 | my $self = shift; 57 | map { $_->all } $self->all_rows; 58 | } 59 | 60 | sub get_col { 61 | my $self = shift; 62 | my ($col) = @_; 63 | map { $_->get($col) } $self->all_rows; 64 | } 65 | 66 | sub set_col { 67 | my $self = shift; 68 | my ($col, $values) = @_; 69 | my @rows = $self->all_rows; 70 | unless (ref $values eq 'ARRAY') { 71 | my @col = (); 72 | push @col, $values for 0..$#rows; 73 | $values = \@col; 74 | } 75 | for my $i (0 .. $#rows) { 76 | $rows[$i]->set($col, $values->[$i]); 77 | } 78 | return 1; 79 | } 80 | 81 | sub add_col { 82 | my $self = shift; 83 | my ($values) = @_; 84 | my @rows = $self->all_rows; 85 | for my $i (0 .. $#rows) { 86 | $rows[$i]->push($values->[$i]); 87 | } 88 | return 1; 89 | } 90 | 91 | sub all_cols { 92 | my $self = shift; 93 | my $col_count = $self->get_row(0)->count; 94 | my $return_type = TypedArray[$CellType]; 95 | return 96 | map { $return_type->coerce($_) } 97 | map { [ $self->get_col($_) ] } 0 .. $col_count-1; 98 | } 99 | 100 | sub to_string { 101 | my $self = shift; 102 | join "\n", map(join("\t", map($_->name, $_->all)), $self->all_rows); 103 | } 104 | 105 | 1; 106 | 107 | __END__ 108 | 109 | =head1 ACKNOWLEDGMENTS 110 | 111 | Code copied and adapted from http://www.perlmonks.org/?node_id=1052124 112 | 113 | Thanks tobyink. 114 | 115 | =cut 116 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Model/ListDataTable.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Model::ListDataTable; 2 | 3 | # ABSTRACT: List Data Table 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use Try::Tiny; 9 | use App::Sqitch::GUI::Types qw( 10 | Int 11 | ); 12 | 13 | use App::Sqitch::X qw(hurl); 14 | use App::Sqitch::GUI::Model::Grid; 15 | 16 | has 'list_data' => ( 17 | is => 'rw', 18 | default => sub { 19 | my $grid = App::Sqitch::GUI::Model::Grid->new( cells => [] ); 20 | }, 21 | ); 22 | 23 | has 'default' => ( 24 | is => 'rw', 25 | isa => Int, 26 | default => sub { 0 }, 27 | ); 28 | 29 | sub set_value { 30 | my $self = shift; 31 | my ($row, $col, $value) = @_; 32 | hurl 'Wrong arguments passed to set_value()' 33 | unless defined $row and defined $col; 34 | $self->list_data->set_cell($row, $col, $value); 35 | return 1; 36 | } 37 | 38 | sub get_value { 39 | my $self = shift; 40 | my ($row, $col) = @_; 41 | hurl 'Wrong arguments passed to get_value()' 42 | unless defined $row and defined $col; 43 | return $self->list_data->get_cell($row, $col)->name; 44 | # for DEBUG? 45 | # my $value = try { $self->list_data->get_cell($row, $col)->name } 46 | # catch { 47 | # warn "'$_'\n"; 48 | # }; 49 | } 50 | 51 | sub get_data_as_string { 52 | my $self = shift; 53 | return $self->list_data->to_string; 54 | } 55 | 56 | sub remove_row { 57 | my $self = shift; 58 | my ($item, $count) = @_; 59 | $count //= 1; 60 | return $self->list_data->remove_row($item, $count); 61 | } 62 | 63 | sub get_item_count { 64 | my $self = shift; 65 | return $self->list_data->rows_no; 66 | } 67 | 68 | sub add_row { 69 | my $self = shift; 70 | $self->list_data->add_row(\@_); 71 | } 72 | 73 | sub get_col { 74 | my $self = shift; 75 | my ($col) = @_; 76 | return $self->list_data->get_col($col); 77 | } 78 | 79 | sub set_col { 80 | my $self = shift; 81 | my ($col, $value) = @_; 82 | return $self->list_data->set_col($col, $value); 83 | } 84 | 85 | 1; 86 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Model/PlanItem.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Model::PlanItem; 2 | 3 | # ABSTRACT: Plan Item model 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | Int 10 | Str 11 | Maybe 12 | ); 13 | 14 | has 'item' => ( 15 | is => 'rw', 16 | isa => Maybe[Int], 17 | ); 18 | 19 | has 'name' => ( 20 | is => 'rw', 21 | isa => Maybe[Str], 22 | ); 23 | 24 | has 'change_id' => ( 25 | is => 'rw', 26 | isa => Maybe[Str], 27 | ); 28 | 29 | 1; 30 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Model/ProjectItem.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Model::ProjectItem; 2 | 3 | # ABSTRACT: Project Item model 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | Int 10 | Str 11 | Dir 12 | Maybe 13 | ); 14 | 15 | has 'item' => ( 16 | is => 'rw', 17 | isa => Maybe[Int], 18 | ); 19 | 20 | has 'name' => ( 21 | is => 'rw', 22 | isa => Maybe[Str], 23 | ); 24 | 25 | has 'path' => ( 26 | is => 'rw', 27 | isa => Maybe[Dir], 28 | ); 29 | 30 | 1; 31 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Options.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Options; 2 | 3 | # ABSTRACT: Options for Sqitch GUI 4 | 5 | use 5.010; 6 | use Moo; 7 | use MooX::Options; 8 | 9 | option 'verbose' => ( 10 | is => 'ro', 11 | doc => 'set verbose on', 12 | short => 'v', 13 | ); 14 | 15 | option 'debug' => ( 16 | is => 'ro', 17 | doc => 'set debug on', 18 | short => 'd', 19 | ); 20 | 21 | 1; 22 | 23 | __END__ 24 | 25 | =encoding utf8 26 | 27 | =head1 SYNOPSIS 28 | 29 | 30 | =head1 DESCRIPTION 31 | 32 | 33 | =head1 INTERFACE 34 | 35 | =head2 ATTRIBUTES 36 | 37 | =head2 C 38 | 39 | =head2 METHODS 40 | 41 | =cut 42 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Refresh.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Refresh; 2 | 3 | # ABSTRACT: An Observer for the GUI 4 | 5 | use 5.010; 6 | use Moo; 7 | use App::Sqitch::GUI::Types qw( 8 | SqitchGUIRules 9 | SqitchGUIView 10 | ); 11 | use App::Sqitch::GUI::Rules; 12 | use namespace::autoclean; 13 | 14 | with 'App::Sqitch::GUI::Roles::Observer'; 15 | 16 | has 'view' => ( 17 | is => 'ro', 18 | isa => SqitchGUIView, 19 | ); 20 | 21 | has 'rules' => ( 22 | is => 'ro', 23 | isa => SqitchGUIRules, 24 | lazy => 1, 25 | builder => '_build_rules', 26 | handles => [ 'get_rules' ], 27 | ); 28 | 29 | sub _build_rules { 30 | return App::Sqitch::GUI::Rules->new; 31 | } 32 | 33 | sub update { 34 | my ( $self, $subject ) = @_; 35 | my $state = $subject->get_state; 36 | $self->view->set_status_bar( $state, $self->get_rules($state) ); 37 | return; 38 | } 39 | 40 | 1; 41 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Roles/Element.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Roles::Element; 2 | 3 | # ABSTRACT: The Element Role 4 | 5 | use Moo::Role; 6 | use App::Sqitch::GUI::Types qw( 7 | HashRef 8 | Maybe 9 | Object 10 | SqitchGUIWxApp 11 | SqitchGUIModel 12 | WxWindow 13 | ); 14 | use namespace::autoclean; 15 | 16 | has 'app' => ( 17 | is => 'ro', 18 | isa => SqitchGUIWxApp, 19 | required => 1, 20 | weak_ref => 1, 21 | ); 22 | 23 | has 'ancestor' => ( 24 | is => 'ro', 25 | isa => Object, 26 | weak_ref => 1, 27 | ); 28 | 29 | has 'parent' => ( 30 | is => 'ro', 31 | isa => Maybe[WxWindow], 32 | ); 33 | 34 | requires '_set_events'; 35 | 36 | after BUILD => sub { 37 | my $self = shift; 38 | $self->_set_events; 39 | return 1; 40 | }; 41 | 42 | 1; 43 | 44 | =head1 AUTHOR 45 | 46 | Stefan Suciu, C<< >> 47 | 48 | =head1 BUGS 49 | 50 | None known. 51 | 52 | Please report any bugs or feature requests to the author. 53 | 54 | =head1 ACKNOWLEDGMENTS 55 | 56 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 57 | project: 58 | 59 | https://github.com/tmtowtdi/LacunaWaX 60 | 61 | Copyright: Jonathan D. Barton 2012-2013 62 | 63 | Thank you! 64 | 65 | =head1 LICENSE AND COPYRIGHT 66 | 67 | Stefan Suciu 2013 68 | 69 | This program is free software; you can redistribute it and/or modify it 70 | under the terms of either: the GNU General Public License as published 71 | by the Free Software Foundation. 72 | 73 | =cut 74 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Roles/Observable.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Roles::Observable; 2 | 3 | # ABSTRACT: Observable role 4 | 5 | use Moo::Role; 6 | use MooX::HandlesVia; 7 | use namespace::autoclean; 8 | 9 | has observers => ( 10 | is => 'ro', 11 | handles_via => 'Array', 12 | default => sub { [] }, 13 | handles => { 14 | 'add_observer' => 'push', 15 | 'count_observers' => 'count', 16 | 'remove_observers' => 'clear', 17 | }, 18 | ); 19 | 20 | sub notify { 21 | my ($self) = @_; 22 | foreach my $observer ( @{ $self->observers } ) { 23 | $observer->update($self); 24 | } 25 | } 26 | 27 | 1; 28 | 29 | __END__ 30 | 31 | =encoding utf8 32 | 33 | =head1 SYNOPSIS 34 | 35 | my $status = App::Sqitch::GUI::Status->new; 36 | $status->add_observer( 37 | App::Sqitch::GUI::Refresh->new( view => $view ) 38 | ); 39 | 40 | =head1 DESCRIPTION 41 | 42 | 43 | =head1 INTERFACE 44 | 45 | =head2 ATTRIBUTES 46 | 47 | =head3 observers 48 | 49 | The observers attribute is an array reference holding the objects 50 | being observed. 51 | 52 | =head2 INSTANCE METHODS 53 | 54 | =head3 add_observer 55 | 56 | Adds an object to the observer list. 57 | 58 | =head3 count_observers 59 | 60 | Counts and returns the number of observed objects. 61 | 62 | =head3 remove_observers 63 | 64 | Removes all observed objects. 65 | 66 | =cut 67 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Roles/Observer.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Roles::Observer; 2 | 3 | # ABSTRACT: Observer role 4 | 5 | use Moo::Role; 6 | use namespace::autoclean; 7 | 8 | requires 'update'; 9 | 10 | 1; 11 | 12 | __END__ 13 | 14 | =encoding utf8 15 | 16 | =head1 SYNOPSIS 17 | 18 | with 'App::Sqitch::GUI::Roles::Observer'; 19 | 20 | sub update { 21 | my ( $self, $subject ) = @_; 22 | # do something with $subject... 23 | return; 24 | } 25 | 26 | =head1 DESCRIPTION 27 | 28 | Basic Moo role to implement the Observer Pattern. 29 | 30 | =head1 INTERFACE 31 | 32 | =head2 ATTRIBUTES 33 | 34 | =head2 INSTANCE METHODS 35 | 36 | =head3 update 37 | 38 | The C method has to be implemented by the class, that consumes 39 | this role. The object being observed is the only argument. 40 | 41 | =cut 42 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Roles/Panel.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Roles::Panel; 2 | 3 | # ABSTRACT: The Panel Role 4 | 5 | use Wx qw(:everything); 6 | use Moo::Role; 7 | use App::Sqitch::X qw(hurl); 8 | use namespace::autoclean; 9 | 10 | sub control_write_stc { 11 | my ( $self, $control, $value, $is_append, $newline ) = @_; 12 | hurl 'Wrong arguments passed to control_write_stc()' 13 | unless $control and $control->isa('Wx::Scintilla::TextCtrl'); 14 | $value ||= q{}; # empty 15 | my $readonly = $control->GetReadOnly; 16 | $control->SetReadOnly(0); 17 | $control->ClearAll unless $is_append; 18 | if ($value) { 19 | $control->AppendText($value); 20 | $control->AppendText("\n") if $newline; 21 | } 22 | $control->Colourise( 0, $control->GetTextLength ); 23 | $control->SetReadOnly($readonly); 24 | return; 25 | } 26 | 27 | sub control_write_e { 28 | my ( $self, $control, $value ) = @_; 29 | hurl 'Wrong arguments passed to control_write_e()' 30 | unless $control and $control->isa('Wx::TextCtrl'); 31 | $control->Clear; 32 | $control->SetValue($value) if defined $value; 33 | return; 34 | } 35 | 36 | sub control_write_c { 37 | my ( $self, $control, $value ) = @_; 38 | hurl 'Wrong arguments passed to control_write_c()' 39 | unless $control 40 | and $control->isa('Wx::ComboBox') 41 | and defined $value; 42 | $control->SetValue($value); 43 | return; 44 | } 45 | 46 | sub control_write_p { 47 | my ( $self, $control, $path ) = @_; 48 | hurl 'Wrong arguments passed to control_write_p()' 49 | unless $control 50 | and $control->isa('Wx::DirPickerCtrl') 51 | and defined $path; 52 | $control->SetPath($path); 53 | return; 54 | } 55 | 56 | sub control_read_e { 57 | my ( $self, $control ) = @_; 58 | hurl 'Wrong arguments passed to control_read_e()' 59 | unless $control and $control->isa('Wx::TextCtrl'); 60 | return $control->GetValue; 61 | } 62 | 63 | sub control_read_p { 64 | my ( $self, $control ) = @_; 65 | hurl 'Wrong arguments passed to control_read_p()' 66 | unless $control 67 | and $control->isa('Wx::DirPickerCtrl'); 68 | return $control->GetPath; 69 | } 70 | 71 | sub control_read_c { 72 | my ( $self, $control ) = @_; 73 | hurl 'Wrong arguments passed to control_read_c()' 74 | unless $control 75 | and $control->isa('Wx::ComboBox'); 76 | return $control->GetValue(); 77 | } 78 | 79 | 1; 80 | 81 | =head1 AUTHOR 82 | 83 | Stefan Suciu, C<< >> 84 | 85 | =head1 BUGS 86 | 87 | None known. 88 | 89 | Please report any bugs or feature requests to the author. 90 | 91 | =head1 LICENSE AND COPYRIGHT 92 | 93 | Stefan Suciu 2015 94 | 95 | This program is free software; you can redistribute it and/or modify it 96 | under the terms of either: the GNU General Public License as published 97 | by the Free Software Foundation. 98 | 99 | =cut 100 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Rules.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Rules; 2 | 3 | # ABSTRACT: Rules for buttons 4 | 5 | use Moo; 6 | use MooX::HandlesVia; 7 | use namespace::autoclean; 8 | 9 | has 'rules' => ( 10 | is => 'ro', 11 | handles_via => 'Hash', 12 | required => 1, 13 | lazy => 1, 14 | default => sub { 15 | { init => { 16 | btn_change => 0, 17 | btn_change_sel => 0, 18 | btn_project => 1, 19 | btn_project_sel => 1, 20 | btn_plan => 0, 21 | btn_plan_sel => 0, 22 | btn_status => 0, 23 | btn_add => 0, 24 | btn_deploy => 0, 25 | btn_revert => 0, 26 | btn_verify => 0, 27 | btn_log => 0, 28 | }, 29 | idle => { 30 | btn_change => 1, 31 | btn_change_sel => 1, 32 | btn_project => 1, 33 | btn_project_sel => 1, 34 | btn_plan => 1, 35 | btn_plan_sel => 1, 36 | btn_status => 1, 37 | btn_add => 0, 38 | btn_deploy => 0, 39 | btn_revert => 0, 40 | btn_verify => 1, 41 | btn_log => 1, 42 | }, 43 | }; 44 | }, 45 | handles => { get_rules => 'get' }, 46 | ); 47 | 48 | 1; 49 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Sqitch.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Sqitch; 2 | 3 | # ABSTRACT: A Sqitch Extension 4 | 5 | use Moo; 6 | use App::Sqitch::GUI::Types qw( 7 | SqitchGUIController 8 | ); 9 | use namespace::autoclean; 10 | 11 | extends 'App::Sqitch'; 12 | 13 | has 'controller' => ( 14 | is => 'ro', 15 | isa => SqitchGUIController, 16 | lazy => 1, 17 | ); 18 | 19 | around 'emit' => sub { 20 | my ($orig, $self) = (shift, shift); 21 | my $msg = join '', @_; 22 | $self->controller->log_message($msg); 23 | }; 24 | 25 | around 'emit_literal' => sub { 26 | my ($orig, $self) = (shift, shift); 27 | my $msg = join '', @_; 28 | $self->controller->log_message($msg); 29 | }; 30 | 31 | around 'vent' => sub { 32 | my ($orig, $self) = (shift, shift); 33 | my $msg = join '', @_; 34 | $self->controller->log_message($msg); 35 | }; 36 | 37 | around 'vent_literal' => sub { 38 | my ($orig, $self) = (shift, shift); 39 | my $msg = join '', @_; 40 | $self->controller->log_message($msg); 41 | }; 42 | 43 | around 'page' => sub { 44 | my ($orig, $self) = (shift, shift); 45 | my $msg = join '', @_; 46 | $self->controller->log_message($msg); 47 | }; 48 | 49 | around 'page_literal' => sub { 50 | my ($orig, $self) = (shift, shift); 51 | my $msg = join '', @_; 52 | $self->controller->log_message($msg); 53 | }; 54 | 55 | 1; 56 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Status.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Status; 2 | 3 | # ABSTRACT: GUI Status 4 | 5 | use Moo; 6 | use Type::Utils qw(enum); 7 | use namespace::autoclean; 8 | 9 | with 'App::Sqitch::GUI::Roles::Observable'; 10 | 11 | has gui_state => ( 12 | is => 'rw', 13 | isa => enum([ qw(load init idle edit) ]), 14 | required => 1, 15 | default => 'load', 16 | ); 17 | 18 | sub set_state { 19 | my ($self, $state) = @_; 20 | $self->gui_state($state); 21 | return $self; 22 | } 23 | 24 | sub get_state { 25 | my $self = shift; 26 | return $self->gui_state; 27 | } 28 | 29 | sub is_state { 30 | my ($self, $state) = @_; 31 | return 1 if $self->gui_state eq $state; 32 | return $self; 33 | } 34 | 35 | after set_state => sub { 36 | my ($self) = @_; 37 | $self->notify(); 38 | }; 39 | 40 | 1; 41 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Target.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Target; 2 | 3 | # ABSTRACT: A Sqitch::Target Extension 4 | 5 | use 5.010; 6 | use Moo; 7 | use App::Sqitch::GUI::Types qw( 8 | Dir 9 | ); 10 | use Path::Class qw(dir file); 11 | use App::Sqitch::X qw(hurl); 12 | use namespace::autoclean; 13 | 14 | extends 'App::Sqitch::Target'; 15 | 16 | has 'top_dir' => ( 17 | is => 'rw', 18 | isa => Dir, 19 | required => 1, 20 | ); 21 | 22 | 1; 23 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Types.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Types; 2 | 3 | # ABSTRACT: Sqitch GUI Types 4 | 5 | use 5.010; 6 | use strict; 7 | use warnings; 8 | use utf8; 9 | use Type::Library 0.040 -base, -declare => qw( 10 | Dir 11 | File 12 | Sqitch 13 | SqitchPlan 14 | SqitchGUIConfig 15 | SqitchGUIController 16 | SqitchGUIDialogProjects 17 | SqitchGUIDialogRules 18 | SqitchGUIDialogStatus 19 | SqitchGUIOptions 20 | SqitchGUIWxListctrl 21 | SqitchGUIModel 22 | SqitchGUIModelListDataTable 23 | SqitchGUIModelPlanItem 24 | SqitchGUIModelProjectItem 25 | SqitchGUIRules 26 | SqitchGUIStatus 27 | SqitchGUITarget 28 | SqitchGUIView 29 | SqitchGUIViewPanelBottom 30 | SqitchGUIViewPanelChange 31 | SqitchGUIViewPanelLeft 32 | SqitchGUIViewPanelPlan 33 | SqitchGUIViewPanelProject 34 | SqitchGUIViewPanelRight 35 | SqitchGUIViewPanelTop 36 | SqitchGUIWxApp 37 | SqitchGUIWxEditor 38 | SqitchGUIWxLogView 39 | SqitchGUIWxNotebook 40 | SqitchGUIWxStatusbar 41 | SqitchGUIWxToolbar 42 | WxAboutDialogInfo 43 | WxButton 44 | WxCollapsiblePane 45 | WxComboBox 46 | WxDirPickerCtrl 47 | WxFrame 48 | WxGridSizer 49 | WxMenuBar 50 | WxPanel 51 | WxPoint 52 | WxRadioButton 53 | WxSize 54 | WxSizer 55 | WxSplitterWindow 56 | WxStaticLine 57 | WxStaticText 58 | WxStatusBar 59 | WxTextCtrl 60 | WxWindow 61 | ); 62 | use Type::Utils -all; 63 | use Types::Standard -types; 64 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 65 | use App::Sqitch::X qw(hurl); 66 | 67 | # Inherit standard types. 68 | BEGIN { extends "Types::Standard" }; 69 | 70 | class_type Sqitch, { class => 'App::Sqitch' }; 71 | class_type SqitchPlan, { class => 'App::Sqitch::Plan' }; 72 | class_type SqitchGUIWxApp, { class => 'App::Sqitch::GUI::WxApp' }; 73 | class_type SqitchGUIView, { class => 'App::Sqitch::GUI::View' }; 74 | class_type SqitchGUIRules, { class => 'App::Sqitch::GUI::Rules' }; 75 | class_type SqitchGUIModel, { class => 'App::Sqitch::GUI::Model' }; 76 | class_type SqitchGUIOptions, { class => 'App::Sqitch::GUI::Options' }; 77 | class_type SqitchGUIModelListDataTable, 78 | { class => 'App::Sqitch::GUI::Model::ListDataTable' }; 79 | class_type SqitchGUIModelProjectItem, 80 | { class => 'App::Sqitch::GUI::Model::ProjectItem' }; 81 | class_type SqitchGUIModelPlanItem, 82 | { class => 'App::Sqitch::GUI::Model::PlanItem' }; 83 | class_type SqitchGUITarget, { class => 'App::Sqitch::GUI::Target' }; 84 | class_type SqitchGUIWxListctrl, { class => 'App::Sqitch::GUI::Wx::Listctrl' }; 85 | class_type SqitchGUIConfig, { class => 'App::Sqitch::GUI::Config' }; 86 | class_type SqitchGUIController, 87 | { class => 'App::Sqitch::GUI::Controller' }; 88 | class_type SqitchGUIStatus, { class => 'App::Sqitch::GUI::Status' }; 89 | class_type SqitchGUIDialogRules, { class => 'App::Sqitch::GUI::View::Dialog::Rules' }; 90 | class_type SqitchGUIDialogStatus, 91 | { class => 'App::Sqitch::GUI::View::Dialog::Status' }; 92 | class_type SqitchGUIDialogProjects, 93 | { class => 'App::Sqitch::GUI::View::Dialog::Projects' }; 94 | class_type SqitchGUIViewPanelLeft, 95 | { class => 'App::Sqitch::GUI::View::Panel::Left' }; 96 | class_type SqitchGUIViewPanelRight, 97 | { class => 'App::Sqitch::GUI::View::Panel::Right' }; 98 | class_type SqitchGUIViewPanelTop, 99 | { class => 'App::Sqitch::GUI::View::Panel::Top' }; 100 | class_type SqitchGUIViewPanelProject, 101 | { class => 'App::Sqitch::GUI::View::Panel::Project' }; 102 | class_type SqitchGUIViewPanelPlan, 103 | { class => 'App::Sqitch::GUI::View::Panel::Plan' }; 104 | class_type SqitchGUIViewPanelChange, 105 | { class => 'App::Sqitch::GUI::View::Panel::Change' }; 106 | class_type SqitchGUIViewPanelBottom, 107 | { class => 'App::Sqitch::GUI::View::Panel::Bottom' }; 108 | class_type SqitchGUIWxToolbar, 109 | { class => 'App::Sqitch::GUI::Wx::Toolbar' }; 110 | class_type SqitchGUIWxStatusbar, 111 | { class => 'App::Sqitch::GUI::Wx::Statusbar' }; 112 | class_type SqitchGUIWxNotebook, 113 | { class => 'App::Sqitch::GUI::Wx::Notebook' }; 114 | class_type SqitchGUIWxEditor, 115 | { class => 'App::Sqitch::GUI::Wx::Editor' }; 116 | class_type SqitchGUIWxLogView, 117 | { class => 'App::Sqitch::GUI::Wx::LogView' }; 118 | 119 | # Wx 120 | class_type WxAboutDialogInfo, { class => 'Wx::AboutDialogInfo' }; 121 | class_type WxButton, { class => 'Wx::Button' }; 122 | class_type WxRadioButton { class => 'Wx::RadioButton' }; 123 | class_type WxComboBox, { class => 'Wx::ComboBox' }; 124 | class_type WxCollapsiblePane, { class => 'Wx::CollapsiblePane' }; 125 | class_type WxDirPickerCtrl, { class => 'Wx::DirPickerCtrl' }; 126 | class_type WxGridSizer, { class => 'Wx::GridSizer' }; 127 | class_type WxMenuBar, { class => 'Wx::MenuBar' }; 128 | class_type WxPanel, { class => 'Wx::Panel' }; 129 | class_type WxPoint, { class => 'Wx::Point' }; 130 | class_type WxSize, { class => 'Wx::Size' }; 131 | class_type WxSizer, { class => 'Wx::Sizer' }; 132 | class_type WxSplitterWindow { class => 'Wx::SplitterWindow' }; 133 | class_type WxStaticLine, { class => 'Wx::StaticLine' }; 134 | class_type WxStaticText, { class => 'Wx::StaticText' }; 135 | class_type WxStatusBar, { class => 'Wx::StatusBar' }; 136 | class_type WxTextCtrl, { class => 'Wx::TextCtrl' }; 137 | class_type WxWindow, { class => 'Wx::Window' }; 138 | class_type WxFrame, { class => 'Wx::Frame' }; 139 | 140 | # Other 141 | class_type File, { class => 'Path::Class::File' }; 142 | class_type Dir, { class => 'Path::Class::Dir' }; 143 | 144 | 1; 145 | 146 | __END__ 147 | 148 | =head1 Name 149 | 150 | App::Sqitch::Types - Definition of attribute data types 151 | 152 | =head1 Synopsis 153 | 154 | use App::Sqitch::Types qw(Bool); 155 | 156 | =head1 Description 157 | 158 | This module defines data types use in Sqitch object attributes. Supported types 159 | are: 160 | 161 | =over 162 | 163 | =item C 164 | 165 | An L object. 166 | 167 | =item C 168 | 169 | An L object. 170 | 171 | =item C 172 | 173 | An L object. 174 | 175 | =item C 176 | 177 | A Sqitch user name. 178 | 179 | =item C 180 | 181 | A Sqitch user email address. 182 | 183 | =item C 184 | 185 | A L object. 186 | 187 | =item C 188 | 189 | A L object. 190 | 191 | =item C 192 | 193 | A L object. 194 | 195 | =item C 196 | 197 | A L object. 198 | 199 | =item C 200 | 201 | A L object. 202 | 203 | =item C 204 | 205 | A L object. 206 | 207 | =item C 208 | 209 | A L object. 210 | 211 | =item C 212 | 213 | A L object. 214 | 215 | =item C 216 | 217 | A L object. 218 | 219 | =item C 220 | 221 | A C object. 222 | 223 | =item C 224 | 225 | A C object. 226 | 227 | =item C 228 | 229 | A L object. 230 | 231 | =item C 232 | 233 | A L database handle. 234 | 235 | =back 236 | 237 | =head1 Author 238 | 239 | David E. Wheeler 240 | 241 | =head1 License 242 | 243 | Copyright (c) 2012-2015 iovation Inc. 244 | 245 | Permission is hereby granted, free of charge, to any person obtaining a copy 246 | of this software and associated documentation files (the "Software"), to deal 247 | in the Software without restriction, including without limitation the rights 248 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 249 | copies of the Software, and to permit persons to whom the Software is 250 | furnished to do so, subject to the following conditions: 251 | 252 | The above copyright notice and this permission notice shall be included in all 253 | copies or substantial portions of the Software. 254 | 255 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 256 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 257 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 258 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 259 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 260 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 261 | SOFTWARE. 262 | 263 | =cut 264 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View; 2 | 3 | # ABSTRACT: The View 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | ArrayRef 10 | Int 11 | Maybe 12 | SqitchGUIModel 13 | SqitchGUIViewPanelBottom 14 | SqitchGUIViewPanelChange 15 | SqitchGUIViewPanelLeft 16 | SqitchGUIViewPanelPlan 17 | SqitchGUIViewPanelProject 18 | SqitchGUIViewPanelRight 19 | SqitchGUIViewPanelTop 20 | SqitchGUIWxStatusbar 21 | SqitchGUIWxToolbar 22 | Str 23 | WxFrame 24 | WxPoint 25 | WxSize 26 | WxSizer 27 | WxSplitterWindow 28 | ); 29 | use Wx qw(:everything); 30 | use Wx::Event qw( 31 | EVT_BUTTON 32 | EVT_CLOSE 33 | EVT_LIST_ITEM_SELECTED 34 | EVT_MENU 35 | EVT_RADIOBUTTON 36 | EVT_TOOL 37 | EVT_TIMER 38 | ); 39 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 40 | use App::Sqitch::X qw(hurl); 41 | 42 | with 'App::Sqitch::GUI::Roles::Element'; 43 | 44 | use App::Sqitch::GUI::Config::Toolbar; 45 | use App::Sqitch::GUI::Wx::Menubar; 46 | use App::Sqitch::GUI::Wx::Toolbar; 47 | use App::Sqitch::GUI::Wx::Statusbar; 48 | use App::Sqitch::GUI::View::Panel::Left; 49 | use App::Sqitch::GUI::View::Panel::Right; 50 | use App::Sqitch::GUI::View::Panel::Top; 51 | use App::Sqitch::GUI::View::Panel::Bottom; 52 | use App::Sqitch::GUI::View::Panel::Change; 53 | use App::Sqitch::GUI::View::Panel::Project; 54 | use App::Sqitch::GUI::View::Panel::Plan; 55 | 56 | # Main window 57 | 58 | # Optional, point: the upper-left corner of the app. 59 | has 'position' => ( 60 | is => 'rw', 61 | isa => Maybe[WxPoint], 62 | ); 63 | 64 | has 'style' => ( 65 | is => 'rw', 66 | isa => Int, 67 | lazy => 1, 68 | builder => '_build_style', 69 | ); 70 | 71 | has 'frame' => ( 72 | is => 'ro', 73 | isa => WxFrame, 74 | lazy => 1, 75 | builder => '_build_frame', 76 | ); 77 | 78 | has 'title' => ( 79 | is => 'rw', 80 | isa => Str, 81 | lazy => 1, 82 | builder => '_build_title', 83 | ); 84 | 85 | has 'size' => ( 86 | is => 'rw', 87 | isa => WxSize, 88 | lazy => 1, 89 | builder => '_build_size', 90 | ); 91 | 92 | has 'tool_bar' => ( 93 | is => 'ro', 94 | isa => SqitchGUIWxToolbar, 95 | lazy => 1, 96 | builder => '_build_tool_bar', 97 | ); 98 | 99 | has 'status_bar' => ( 100 | is => 'ro', 101 | isa => SqitchGUIWxStatusbar, 102 | lazy => 1, 103 | builder => '_build_status_bar', 104 | ); 105 | 106 | # Panels 107 | 108 | has 'left' => ( 109 | is => 'ro', 110 | isa => SqitchGUIViewPanelLeft, 111 | lazy => 1, 112 | builder => '_build_left', 113 | ); 114 | 115 | has 'right' => ( 116 | is => 'ro', 117 | isa => SqitchGUIViewPanelRight, 118 | lazy => 1, 119 | builder => '_build_right', 120 | ); 121 | 122 | has 'top' => ( 123 | is => 'ro', 124 | isa => SqitchGUIViewPanelTop, 125 | lazy => 1, 126 | builder => '_build_top', 127 | ); 128 | 129 | has 'project' => ( 130 | is => 'ro', 131 | isa => SqitchGUIViewPanelProject, 132 | lazy => 1, 133 | builder => '_build_project', 134 | ); 135 | 136 | has 'plan' => ( 137 | is => 'ro', 138 | isa => SqitchGUIViewPanelPlan, 139 | lazy => 1, 140 | builder => '_build_plan', 141 | ); 142 | 143 | has 'change' => ( 144 | is => 'ro', 145 | isa => SqitchGUIViewPanelChange, 146 | lazy => 1, 147 | builder => '_build_change', 148 | ); 149 | 150 | has 'bottom' => ( 151 | is => 'ro', 152 | isa => SqitchGUIViewPanelBottom, 153 | lazy => 1, 154 | builder => '_build_bottom', 155 | ); 156 | 157 | # Sizers 158 | has 'main_sizer' => ( 159 | is => 'ro', 160 | isa => WxSizer, 161 | lazy => 1, 162 | builder => '_build_main_sizer', 163 | ); 164 | 165 | # Miscellaneous 166 | has 'min_pane_size' => ( 167 | is => 'ro', 168 | isa => Int, 169 | lazy => 1, 170 | default => 50, 171 | ); 172 | 173 | has 'sash_pos' => ( 174 | is => 'ro', 175 | isa => Int, 176 | lazy => 1, 177 | default => 450, 178 | ); 179 | 180 | # Splitter window 181 | has 'splitter_w' => ( 182 | is => 'ro', 183 | isa => WxSplitterWindow, 184 | lazy => 1, 185 | builder => '_build_splitter_w', 186 | ); 187 | 188 | has 'model' => ( 189 | is => 'ro', 190 | isa => SqitchGUIModel, 191 | lazy => 1, 192 | default => sub { 193 | shift->app->model; 194 | }, 195 | ); 196 | 197 | sub BUILD { 198 | my($self, @params) = @_; 199 | 200 | #Wx::EnableDefaultAssertHandler(); # Debug 201 | 202 | $self->frame->Hide; 203 | 204 | # Menu Bar 205 | my $menu = App::Sqitch::GUI::Wx::Menubar->new( 206 | app => $self->app, 207 | ancestor => $self, 208 | parent => $self->frame, 209 | ); 210 | $self->frame->SetMenuBar( $menu->menu_bar ); 211 | 212 | # Tool Bar 213 | my $conf = App::Sqitch::GUI::Config::Toolbar->new; 214 | my @toolbars = $conf->all_buttons; 215 | my $tb = $self->tool_bar; 216 | foreach my $name (@toolbars) { 217 | my $attribs = $conf->get_tool($name); 218 | $tb->make_toolbar_button( $name, $attribs ); 219 | } 220 | $tb->set_initial_mode( \@toolbars ); 221 | $self->frame->SetToolBar($tb); 222 | $tb->Realize; 223 | 224 | $self->main_sizer->Add( $self->left->panel, 1, wxEXPAND | wxALL, 0 ); 225 | $self->main_sizer->Add( $self->right->panel, 0, wxEXPAND | wxALL, 0 ); 226 | 227 | $self->left->sizer->Add( $self->splitter_w, 1, wxEXPAND | wxALL, 0 ); 228 | 229 | $self->splitter_w->SplitHorizontally( $self->top->panel, 230 | $self->bottom->panel, 231 | $self->sash_pos ); 232 | $self->splitter_w->SetMinimumPaneSize( $self->min_pane_size ); 233 | 234 | $self->top->sizer->Add( $self->change->panel, 1, wxEXPAND | wxALL, 0 ); 235 | $self->top->sizer->Add( $self->project->panel, 1, wxEXPAND | wxALL, 0 ); 236 | $self->top->sizer->Add( $self->plan->panel, 1, wxEXPAND | wxALL, 0 ); 237 | 238 | $self->top->panel->SetSizer( $self->top->sizer ); 239 | 240 | $self->frame->SetSizer( $self->main_sizer ); 241 | 242 | if ($^O ne 'MSWin32') { 243 | $self->change->panel->Show; # Gtk-WARNINGs if default is not Change 244 | # later set to Project... ;) 245 | } 246 | else { 247 | $self->project->panel->Show; 248 | } 249 | 250 | $self->frame->Show; 251 | 252 | return $self; 253 | } 254 | 255 | sub _build_frame { 256 | my $self = shift; 257 | my $y = Wx::Frame->new( 258 | undef, -1, 259 | $self->title, 260 | $self->position || [-1, -1], 261 | $self->size, 262 | $self->style, 263 | ); 264 | $y->Centre() unless $self->position; 265 | return $y; 266 | } 267 | 268 | sub _build_tool_bar { 269 | my $self = shift; 270 | my $tb = App::Sqitch::GUI::Wx::Toolbar->new( 271 | app => $self->app, 272 | ancestor => $self, 273 | parent => $self->frame, 274 | icon_path => $self->app->config->icon_path, 275 | ); 276 | return $tb; 277 | } 278 | 279 | sub _build_status_bar { 280 | my $self = shift; 281 | my $sb = App::Sqitch::GUI::Wx::Statusbar->new( 282 | app => $self->app, 283 | ancestor => $self, 284 | parent => $self->frame, 285 | ); 286 | return $sb; 287 | } 288 | 289 | sub _build_size { 290 | return Wx::Size->new(1024, 768); # default window size 291 | } 292 | 293 | sub _build_style { 294 | my $self = shift; 295 | 296 | return wxCAPTION 297 | | wxCLOSE_BOX 298 | | wxMINIMIZE_BOX 299 | | wxMAXIMIZE_BOX 300 | | wxSYSTEM_MENU 301 | | wxRESIZE_BORDER 302 | | wxCLIP_CHILDREN; 303 | } 304 | 305 | sub _build_title { 306 | my $self = shift; 307 | return 'Sqitch Title'; 308 | } 309 | 310 | sub _build_main_sizer { 311 | return Wx::BoxSizer->new(wxHORIZONTAL); 312 | } 313 | 314 | sub _build_top_sizer { 315 | return Wx::BoxSizer->new(wxHORIZONTAL); 316 | } 317 | 318 | sub _build_left { 319 | my $self = shift; 320 | 321 | my $panel = App::Sqitch::GUI::View::Panel::Left->new( 322 | app => $self->app, 323 | parent => $self->frame, 324 | ancestor => $self, 325 | ); 326 | 327 | return $panel; 328 | } 329 | 330 | sub _build_right { 331 | my $self = shift; 332 | 333 | my $panel = App::Sqitch::GUI::View::Panel::Right->new( 334 | app => $self->app, 335 | parent => $self->frame, 336 | ancestor => $self, 337 | ); 338 | 339 | return $panel; 340 | } 341 | 342 | sub _build_top { 343 | my $self = shift; 344 | 345 | return App::Sqitch::GUI::View::Panel::Top->new( 346 | app => $self->app, 347 | parent => $self->splitter_w, 348 | ancestor => $self, 349 | ); 350 | } 351 | 352 | sub _build_project { 353 | my $self = shift; 354 | 355 | return App::Sqitch::GUI::View::Panel::Project->new( 356 | app => $self->app, 357 | parent => $self->top->panel, 358 | ancestor => $self, 359 | ); 360 | } 361 | 362 | sub _build_plan { 363 | my $self = shift; 364 | return App::Sqitch::GUI::View::Panel::Plan->new( 365 | app => $self->app, 366 | parent => $self->top->panel, 367 | ancestor => $self, 368 | list_data => $self->model->plan_list_data, 369 | ); 370 | } 371 | 372 | sub _build_change { 373 | my $self = shift; 374 | 375 | return App::Sqitch::GUI::View::Panel::Change->new( 376 | app => $self->app, 377 | parent => $self->top->panel, 378 | ancestor => $self, 379 | ); 380 | } 381 | 382 | sub _build_bottom { 383 | my $self = shift; 384 | 385 | return App::Sqitch::GUI::View::Panel::Bottom->new( 386 | app => $self->app, 387 | parent => $self->splitter_w, 388 | ancestor => $self, 389 | ); 390 | } 391 | 392 | sub _build_splitter_w { 393 | my $self = shift; 394 | 395 | my $spw = Wx::SplitterWindow->new( 396 | $self->left->panel, 397 | -1, 398 | [ -1, -1 ], 399 | [ -1, -1 ], 400 | wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN 401 | ); 402 | 403 | return $spw; 404 | } 405 | 406 | sub _set_events { 407 | my $self = shift; 408 | 409 | EVT_CLOSE( $self->frame, sub { $self->OnClose(@_) } ); 410 | 411 | foreach my $name ( qw{change project plan} ) { 412 | my $btn = q{btn_} . $name; 413 | EVT_BUTTON $self->frame, $self->right->$btn->GetId, sub { 414 | $self->show_panel($name); 415 | }; 416 | 417 | my $btn_sel = q{btn_} . $name . q{_sel}; 418 | EVT_RADIOBUTTON $self->frame, $self->right->$btn_sel->GetId, 419 | sub { $self->on_radio($name); }; 420 | } 421 | 422 | return 1; 423 | } 424 | 425 | sub show_panel { 426 | my ($self, $name) = @_; 427 | 428 | foreach my $panel ( qw{change project plan} ) { 429 | $self->$panel->panel->Show 430 | unless $self->$panel->panel->IsShown and $panel eq $name; 431 | 432 | $self->$panel->panel->Hide 433 | if $self->$panel->panel->IsShown and $panel ne $name; 434 | } 435 | 436 | my $btn_sel = q{btn_} . $name . q{_sel}; 437 | $self->right->$btn_sel->SetValue(1); 438 | 439 | $self->top->panel->Layout(); 440 | 441 | return; 442 | } 443 | 444 | sub set_status_bar { 445 | my ( $self, $state, $gui_rules ) = @_; 446 | 447 | $self->status_bar->change_caption( $state, 1 ); 448 | foreach my $btn ( keys %{$gui_rules} ) { 449 | my $enable = $gui_rules->{$btn}; 450 | $self->right->$btn->Enable($enable); 451 | } 452 | my $timer = Wx::Timer->new( $self->frame, 1 ); 453 | $timer->Start( 200, 1 ); # one shot 454 | EVT_TIMER $self->frame, 1, sub { 455 | $self->show_panel('project'); 456 | }; 457 | return; 458 | } 459 | 460 | sub on_radio { 461 | my( $self, $name ) = @_; 462 | $self->show_panel($name); 463 | return; 464 | } 465 | 466 | sub OnClose { 467 | my ($self, $frame, $event) = @_; 468 | $event->Skip(); 469 | return; 470 | } 471 | 472 | sub get_toolbar_btn { 473 | my ( $self, $name ) = @_; 474 | return $self->tool_bar->get_toolbar_btn($name); 475 | } 476 | 477 | sub event_handler_for_tb_button { 478 | my ( $self, $name, $calllback ) = @_; 479 | my $tb_id = $self->get_toolbar_btn($name)->GetId; 480 | EVT_TOOL $self->frame, $tb_id, $calllback; 481 | return; 482 | } 483 | 484 | sub event_handler_for_list { 485 | my ( $self, $list, $calllback ) = @_; 486 | EVT_LIST_ITEM_SELECTED $self->frame, $list, $calllback; 487 | return; 488 | } 489 | 490 | sub load_txt_form_for { 491 | my ($self, $form, $field, $value) = @_; 492 | hurl 'Wrong arguments passed to load_txt_form_for()' 493 | unless defined $field; 494 | my $name = "txt_$field"; 495 | my $control = $self->$form->$name; 496 | $self->$form->control_write_e($control, $value); 497 | return; 498 | } 499 | 500 | sub load_sql_form_for { 501 | my ($self, $form, $command, $value) = @_; 502 | my $name = "edit_$command"; 503 | my $control = $self->$form->$name; 504 | $self->$form->control_write_stc($control, $value); 505 | } 506 | 507 | sub get_plan_list_ctrl { 508 | my $self = shift; 509 | return $self->plan->list_ctrl; 510 | } 511 | 512 | sub get_project_list_ctrl { 513 | my $self = shift; 514 | return $self->project->list_ctrl; 515 | } 516 | 517 | sub log_message { 518 | my ($self, $msg, $newline) = @_; 519 | my $control = $self->bottom->log_ctrl; 520 | $self->bottom->control_write_stc($control, $msg, 'append', $newline); 521 | $control->ScrollToLine( $control->GetLineCount ); 522 | return; 523 | } 524 | 525 | =head1 PANELS 526 | 527 | +-------------------------------------------------------------------+ 528 | | +--Left---------------------------------------------+ +-Right--+ | 529 | | | +----------------------------------------Top----+ | | | | 530 | | | | +--Project/Change/Status--------------------+ | | | | | 531 | | | | | | | | | | | 532 | | | | | | | | | | | 533 | | | | | | | | | | | 534 | | | | | | | | | | | 535 | | | | | | | | | | | 536 | | | | | | | | | | | 537 | | | | | | | | | | | 538 | | | | +-------------------------------------------' | | | | | 539 | | | +-----------------------------------------------' | | | | 540 | | | +----------------------------------------Bottom-+ | | | | 541 | | | | | | | | | 542 | | | | | | | | | 543 | | | | | | | | | 544 | | | +-----------------------------------------------+ | | | | 545 | | +---------------------------------------------------+ +--------+ | 546 | +-------------------------------------------------------------------+ 547 | 548 | The sizer on C<< View::Panel::Top >> holds the 549 | Project/Change/Status panels. This panels are initially hidden and are 550 | showed one by one using the buttons on the right panel, like a 551 | notebook, but with independent buttons. 552 | 553 | =head1 AUTHOR 554 | 555 | Stefan Suciu, C<< >> 556 | 557 | =head1 BUGS 558 | 559 | None known. 560 | 561 | Please report any bugs or feature requests to the author. 562 | 563 | =head1 ACKNOWLEDGMENTS 564 | 565 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 566 | project: 567 | 568 | https://github.com/tmtowtdi/LacunaWaX 569 | 570 | Copyright: Jonathan D. Barton 2012-2013 571 | 572 | Thank you! 573 | 574 | =head1 LICENSE AND COPYRIGHT 575 | 576 | Stefan Suciu 2013 577 | 578 | This program is free software; you can redistribute it and/or modify it 579 | under the terms of either: the GNU General Public License as published 580 | by the Free Software Foundation. 581 | 582 | =cut 583 | 584 | 1; # End of App::Sqitch::GUI::View 585 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Dialog/About.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Dialog::About; 2 | 3 | # ABSTRACT: The About Dialog 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | WxAboutDialogInfo 10 | ); 11 | use Wx qw(wxVERSION_STRING); 12 | 13 | with 'App::Sqitch::GUI::Roles::Element'; 14 | 15 | has 'info' => ( 16 | is => 'ro', 17 | isa => WxAboutDialogInfo, 18 | default => sub { 19 | return Wx::AboutDialogInfo->new; 20 | }, 21 | ); 22 | 23 | sub BUILD { 24 | my $self = shift; 25 | 26 | my $PROGRAM_NAME = q{ Sqitch GUI }; 27 | my $WX_VERSION = wxVERSION_STRING; 28 | my $PROGRAM_DESC 29 | = qq{\nA GUI for Sqitch.\n} . qq{\nwxPerl $Wx::VERSION, $WX_VERSION\n} 30 | . qq{Sqitch $App::Sqitch::VERSION}; 31 | my $PROGRAM_VER = $App::Sqitch::GUI::VERSION // q{(devel)}; 32 | $self->info->SetName($PROGRAM_NAME); 33 | $self->info->SetVersion($PROGRAM_VER); 34 | $self->info->SetDescription($PROGRAM_DESC); 35 | $self->info->SetCopyright(qq{Copyright 2016 Ștefan Suciu} 36 | . qq{\n} 37 | . qq{Copyright 2012-2016 iovation Inc.}); 38 | $self->info->SetLicense(q{ 39 | Permission is hereby granted, free of charge, to any person obtaining 40 | a copy of this software and associated documentation files (the 41 | "Software"), to deal in the Software without restriction, including 42 | without limitation the rights to use, copy, modify, merge, publish, 43 | distribute, sublicense, and/or sell copies of the Software, and to 44 | permit persons to whom the Software is furnished to do so, subject to 45 | the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be 48 | included in all copies or substantial portions of the Software. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 51 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 52 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 53 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 54 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 55 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 56 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 57 | }); 58 | $self->info->AddDeveloper('Ștefan Suciu'); 59 | $self->info->AddDeveloper('David E. Wheeler'); 60 | 61 | return $self; 62 | } 63 | 64 | sub _set_events { } 65 | 66 | sub show { 67 | my $self = shift; 68 | Wx::AboutBox($self->info); 69 | return; 70 | } 71 | 72 | =head1 AUTHOR 73 | 74 | Stefan Suciu, C<< >> 75 | 76 | =head1 BUGS 77 | 78 | None known. 79 | 80 | Please report any bugs or feature requests to the author. 81 | 82 | =head1 ACKNOWLEDGMENTS 83 | 84 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 85 | project: 86 | 87 | https://github.com/tmtowtdi/LacunaWaX 88 | 89 | Copyright: Jonathan D. Barton 2012-2013 90 | 91 | Thank you! 92 | 93 | =head1 LICENSE AND COPYRIGHT 94 | 95 | Stefan Suciu 2013 96 | 97 | This program is free software; you can redistribute it and/or modify it 98 | under the terms of either: the GNU General Public License as published 99 | by the Free Software Foundation. 100 | 101 | =cut 102 | 103 | 1; # End of App::Sqitch::GUI::View::Dialog::About 104 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Dialog/Help.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Dialog::Help; 2 | 3 | # ABSTRACT: The Help Dialog 4 | 5 | use 5.010; 6 | use Moo; 7 | 8 | with 'App::Sqitch::GUI::Roles::Element'; 9 | 10 | sub BUILD { 11 | my $self = shift; 12 | return $self; 13 | } 14 | 15 | sub _set_events { } 16 | 17 | =head1 AUTHOR 18 | 19 | Stefan Suciu, C<< >> 20 | 21 | =head1 BUGS 22 | 23 | None known. 24 | 25 | Please report any bugs or feature requests to the author. 26 | 27 | =head1 ACKNOWLEDGMENTS 28 | 29 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 30 | project: 31 | 32 | https://github.com/tmtowtdi/LacunaWaX 33 | 34 | Copyright: Jonathan D. Barton 2012-2013 35 | 36 | Thank you! 37 | 38 | =head1 LICENSE AND COPYRIGHT 39 | 40 | Stefan Suciu 2013 41 | 42 | This program is free software; you can redistribute it and/or modify it 43 | under the terms of either: the GNU General Public License as published 44 | by the Free Software Foundation. 45 | 46 | =cut 47 | 48 | 1; # End of App::Sqitch::GUI::View::Dialog::Help 49 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Dialog/Refresh.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Dialog::Refresh; 2 | 3 | # ABSTRACT: An Observer for the Projects Dialog 4 | 5 | use 5.010; 6 | use Moo; 7 | use App::Sqitch::GUI::Types qw( 8 | SqitchGUIDialogProjects 9 | SqitchGUIDialogRules 10 | ); 11 | use App::Sqitch::GUI::View::Dialog::Rules; 12 | use namespace::autoclean; 13 | 14 | with 'App::Sqitch::GUI::Roles::Observer'; 15 | 16 | has 'dialog' => ( 17 | is => 'ro', 18 | isa => SqitchGUIDialogProjects, 19 | ); 20 | 21 | has 'rules' => ( 22 | is => 'ro', 23 | isa => SqitchGUIDialogRules, 24 | lazy => 1, 25 | builder => '_build_rules', 26 | ); 27 | 28 | sub _build_rules { 29 | return App::Sqitch::GUI::View::Dialog::Rules->new; 30 | } 31 | 32 | sub update { 33 | my ( $self, $subject ) = @_; 34 | my $state = $subject->get_state; 35 | $self->dialog->set_button_status($state, $self->rules->get_rules($state) ); 36 | return; 37 | } 38 | 39 | 1; 40 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Dialog/Rules.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Dialog::Rules; 2 | 3 | # ABSTRACT: Rules for the Projects Dialog 4 | 5 | use Moo; 6 | use MooX::HandlesVia; 7 | use namespace::autoclean; 8 | 9 | has 'rules' => ( 10 | is => 'ro', 11 | handles_via => 'Hash', 12 | required => 1, 13 | lazy => 1, 14 | default => sub { 15 | { init => { 16 | btn_new => 0, 17 | btn_add => 0, 18 | btn_remove => 0, 19 | btn_save => 0, 20 | btn_close => 1, 21 | txt_name => 0, 22 | dpc_path => 0, 23 | cbx_engine => 0, 24 | txt_db => 0, 25 | }, 26 | sele => { 27 | btn_new => 1, 28 | btn_add => 1, 29 | btn_remove => 1, 30 | btn_save => 0, 31 | btn_close => 1, 32 | txt_name => 0, 33 | dpc_path => 0, 34 | cbx_engine => 0, 35 | txt_db => 0, 36 | }, 37 | new => { 38 | btn_new => 1, 39 | btn_add => 0, 40 | btn_remove => 0, 41 | btn_save => 1, 42 | btn_close => 0, 43 | txt_name => 1, 44 | dpc_path => 1, 45 | cbx_engine => 0, 46 | txt_db => 1, 47 | }, 48 | add => { 49 | btn_new => 0, 50 | btn_add => 1, 51 | btn_remove => 0, 52 | btn_save => 1, 53 | btn_close => 0, 54 | txt_name => 1, 55 | dpc_path => 1, 56 | cbx_engine => 0, 57 | txt_db => 1, 58 | }, 59 | }; 60 | }, 61 | handles => { get_rules => 'get' }, 62 | ); 63 | 64 | 1; 65 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Dialog/Status.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Dialog::Status; 2 | 3 | # ABSTRACT: Projects Dialog Status 4 | 5 | use Moo; 6 | use Type::Utils qw(enum); 7 | use namespace::autoclean; 8 | 9 | with 'App::Sqitch::GUI::Roles::Observable'; 10 | 11 | has dlg_state => ( 12 | is => 'rw', 13 | isa => enum([ qw(init idle sele edit add new) ]), 14 | required => 1, 15 | default => 'init', 16 | ); 17 | 18 | sub set_state { 19 | my ($self, $state) = @_; 20 | $self->dlg_state($state); 21 | return $self; 22 | } 23 | 24 | sub get_state { 25 | my $self = shift; 26 | return $self->dlg_state; 27 | } 28 | 29 | sub is_state { 30 | my ($self, $state) = @_; 31 | return 1 if $self->dlg_state eq $state; 32 | return $self; 33 | } 34 | 35 | after set_state => sub { 36 | my ($self) = @_; 37 | $self->notify(); 38 | }; 39 | 40 | 1; 41 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Panel/Bottom.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Panel::Bottom; 2 | 3 | # ABSTRACT: The Bottom Panel 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | ArrayRef 10 | Maybe 11 | Object 12 | SqitchGUIWxLogView 13 | WxPanel 14 | WxSizer 15 | WxTextCtrl 16 | ); 17 | use Wx qw(:allclasses :everything); 18 | use Wx::Event qw(EVT_CLOSE); 19 | 20 | with qw(App::Sqitch::GUI::Roles::Element 21 | App::Sqitch::GUI::Roles::Panel); 22 | 23 | use App::Sqitch::GUI::Wx::LogView; 24 | 25 | has 'panel' => ( 26 | is => 'ro', 27 | isa => WxPanel, 28 | lazy => 1, 29 | builder => '_build_panel', 30 | ); 31 | 32 | has 'sizer' => ( 33 | is => 'ro', 34 | isa => WxSizer, 35 | lazy => 1, 36 | builder => '_build_sizer', 37 | ); 38 | 39 | has 'log_ctrl' => ( 40 | is => 'ro', 41 | isa => SqitchGUIWxLogView, 42 | lazy => 1, 43 | builder => '_build_log_ctrl', 44 | ); 45 | 46 | sub _build_log_ctrl { 47 | my $self = shift; 48 | my $log_ctrl = App::Sqitch::GUI::Wx::LogView->new( 49 | app => $self->app, 50 | parent => $self->panel, 51 | ancestor => $self, 52 | ); 53 | $log_ctrl->SetReadOnly(1); # log is readonly 54 | return $log_ctrl; 55 | } 56 | 57 | sub BUILD { 58 | my $self = shift; 59 | 60 | #- The main panel 61 | 62 | $self->panel->Show(0); 63 | $self->panel->SetSizer( $self->sizer ); 64 | 65 | #-- Log control on the left-bottom side 66 | 67 | $self->sizer->Add( $self->log_ctrl, 1, wxEXPAND | wxALL, 5 ); 68 | 69 | #-- 70 | 71 | $self->panel->Show(1); 72 | 73 | return $self; 74 | } 75 | 76 | sub _build_panel { 77 | my $self = shift; 78 | 79 | my $panel = Wx::Panel->new( 80 | $self->parent, 81 | -1, 82 | [ -1, -1 ], 83 | [ -1, -1 ], 84 | wxFULL_REPAINT_ON_RESIZE, 85 | 'mainPanel', 86 | ); 87 | #$panel->SetBackgroundColour(Wx::Colour->new('blue')); 88 | #$panel->SetBackgroundColour( $self->parent->GetBackgroundColour ); 89 | 90 | return $panel; 91 | } 92 | 93 | sub _build_sizer { 94 | return Wx::BoxSizer->new(wxVERTICAL); 95 | } 96 | 97 | sub _set_events { } 98 | 99 | =head1 AUTHOR 100 | 101 | Stefan Suciu, C<< >> 102 | 103 | =head1 BUGS 104 | 105 | None known. 106 | 107 | Please report any bugs or feature requests to the author. 108 | 109 | =head1 ACKNOWLEDGMENTS 110 | 111 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 112 | project: 113 | 114 | https://github.com/tmtowtdi/LacunaWaX 115 | 116 | Copyright: Jonathan D. Barton 2012-2013 117 | 118 | Thank you! 119 | 120 | =head1 LICENSE AND COPYRIGHT 121 | 122 | Stefan Suciu 2013 123 | 124 | This program is free software; you can redistribute it and/or modify it 125 | under the terms of either: the GNU General Public License as published 126 | by the Free Software Foundation. 127 | 128 | =cut 129 | 130 | 1; # End of App::Sqitch::GUI::View::Panel::Bottom 131 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Panel/Change.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Panel::Change; 2 | 3 | # ABSTRACT: The Change Panel 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | SqitchGUIWxEditor 10 | SqitchGUIWxNotebook 11 | WxCollapsiblePane 12 | WxPanel 13 | WxSizer 14 | WxStaticText 15 | WxTextCtrl 16 | ); 17 | use Wx qw(:allclasses :everything); 18 | use Wx::Event qw(EVT_CLOSE EVT_COLLAPSIBLEPANE_CHANGED); 19 | 20 | with qw(App::Sqitch::GUI::Roles::Element 21 | App::Sqitch::GUI::Roles::Panel); 22 | 23 | use App::Sqitch::GUI::Wx::Notebook; 24 | use App::Sqitch::GUI::Wx::Editor; 25 | 26 | has 'panel' => ( 27 | is => 'ro', 28 | isa => WxPanel, 29 | lazy => 1, 30 | builder => '_build_panel', 31 | ); 32 | 33 | has 'sizer' => ( 34 | is => 'ro', 35 | isa => WxSizer, 36 | lazy => 1, 37 | builder => '_build_sizer', 38 | ); 39 | 40 | has 'top_sizer' => ( 41 | is => 'ro', 42 | isa => WxSizer, 43 | lazy => 1, 44 | builder => '_build_top_sizer', 45 | ); 46 | 47 | has 'colpsizer' => ( 48 | is => 'ro', 49 | isa => WxSizer, 50 | lazy => 1, 51 | builder => '_build_colpsizer', 52 | ); 53 | 54 | has 'collpane' => ( 55 | is => 'ro', 56 | isa => WxCollapsiblePane, 57 | lazy => 1, 58 | builder => '_build_collpane', 59 | ); 60 | 61 | has 'main_fg_sz' => ( 62 | is => 'ro', 63 | isa => WxSizer, 64 | lazy => 1, 65 | builder => '_build_main_fg_sz', 66 | ); 67 | 68 | has 'form_fg_sz' => ( 69 | is => 'ro', 70 | isa => WxSizer, 71 | lazy => 1, 72 | builder => '_build_form_fg_sz', 73 | ); 74 | 75 | has 'lbl_change_id' => ( 76 | is => 'ro', 77 | isa => WxStaticText, 78 | lazy => 1, 79 | builder => '_build_lbl_change_id', 80 | ); 81 | 82 | has 'lbl_name' => ( 83 | is => 'ro', 84 | isa => WxStaticText, 85 | lazy => 1, 86 | builder => '_build_lbl_name', 87 | ); 88 | 89 | has 'lbl_note' => ( 90 | is => 'ro', 91 | isa => WxStaticText, 92 | lazy => 1, 93 | builder => '_build_lbl_note', 94 | ); 95 | 96 | has 'lbl_committed_at' => ( 97 | is => 'ro', 98 | isa => WxStaticText, 99 | lazy => 1, 100 | builder => '_build_lbl_committed_at', 101 | ); 102 | 103 | has 'lbl_committer_name' => ( 104 | is => 'ro', 105 | isa => WxStaticText, 106 | lazy => 1, 107 | builder => '_build_lbl_committer_name', 108 | ); 109 | 110 | has 'lbl_committer_email' => ( 111 | is => 'ro', 112 | isa => WxStaticText, 113 | lazy => 1, 114 | builder => '_build_lbl_committer_email', 115 | ); 116 | 117 | has 'lbl_planned_at' => ( 118 | is => 'ro', 119 | isa => WxStaticText, 120 | lazy => 1, 121 | builder => '_build_lbl_planned_at', 122 | ); 123 | 124 | has 'lbl_planner_name' => ( 125 | is => 'ro', 126 | isa => WxStaticText, 127 | lazy => 1, 128 | builder => '_build_lbl_planner_name', 129 | ); 130 | 131 | has 'lbl_planner_email' => ( 132 | is => 'ro', 133 | isa => WxStaticText, 134 | lazy => 1, 135 | builder => '_build_lbl_planner_email', 136 | ); 137 | 138 | has 'txt_change_id' => ( 139 | is => 'ro', 140 | isa => WxTextCtrl, 141 | lazy => 1, 142 | builder => '_build_txt_change_id', 143 | ); 144 | 145 | has 'txt_name' => ( 146 | is => 'ro', 147 | isa => WxTextCtrl, 148 | lazy => 1, 149 | builder => '_build_txt_name', 150 | ); 151 | 152 | has 'txt_note' => ( 153 | is => 'ro', 154 | isa => WxTextCtrl, 155 | lazy => 1, 156 | builder => '_build_txt_note', 157 | ); 158 | 159 | has 'txt_committed_at' => ( 160 | is => 'ro', 161 | isa => WxTextCtrl, 162 | lazy => 1, 163 | builder => '_build_txt_committed_at', 164 | ); 165 | 166 | has 'txt_committer_name' => ( 167 | is => 'ro', 168 | isa => WxTextCtrl, 169 | lazy => 1, 170 | builder => '_build_txt_committer_name', 171 | ); 172 | 173 | has 'txt_committer_email' => ( 174 | is => 'ro', 175 | isa => WxTextCtrl, 176 | lazy => 1, 177 | builder => '_build_txt_committer_email', 178 | ); 179 | 180 | has 'txt_planned_at' => ( 181 | is => 'ro', 182 | isa => WxTextCtrl, 183 | lazy => 1, 184 | builder => '_build_txt_planned_at', 185 | ); 186 | 187 | has 'txt_planner_name' => ( 188 | is => 'ro', 189 | isa => WxTextCtrl, 190 | lazy => 1, 191 | builder => '_build_txt_planner_name', 192 | ); 193 | 194 | has 'txt_planner_email' => ( 195 | is => 'ro', 196 | isa => WxTextCtrl, 197 | lazy => 1, 198 | builder => '_build_txt_planner_email', 199 | ); 200 | 201 | has 'sb_sizer' => ( 202 | is => 'ro', 203 | isa => WxSizer, 204 | lazy => 1, 205 | builder => '_build_sb_sizer', 206 | ); 207 | 208 | has 'deploy_sz' => ( 209 | is => 'ro', 210 | isa => WxSizer, 211 | lazy => 1, 212 | builder => '_build_deploy_sz', 213 | ); 214 | 215 | has 'verify_sz' => ( 216 | is => 'ro', 217 | isa => WxSizer, 218 | lazy => 1, 219 | builder => '_build_verify_sz', 220 | ); 221 | 222 | has 'revert_sz' => ( 223 | is => 'ro', 224 | isa => WxSizer, 225 | lazy => 1, 226 | builder => '_build_revert_sz', 227 | ); 228 | 229 | has 'notebook' => ( 230 | is => 'ro', 231 | isa => SqitchGUIWxNotebook, 232 | lazy => 1, 233 | builder => '_build_notebook', 234 | ); 235 | 236 | has 'edit_deploy' => ( 237 | is => 'ro', 238 | isa => SqitchGUIWxEditor, 239 | lazy => 1, 240 | builder => '_build_edit_deploy', 241 | ); 242 | 243 | has 'edit_revert' => ( 244 | is => 'ro', 245 | isa => SqitchGUIWxEditor, 246 | lazy => 1, 247 | builder => '_build_edit_revert', 248 | ); 249 | 250 | has 'edit_verify' => ( 251 | is => 'ro', 252 | isa => SqitchGUIWxEditor, 253 | lazy => 1, 254 | builder => '_build_edit_verify', 255 | ); 256 | 257 | has 'ed_deploy_sbs' => ( 258 | is => 'ro', 259 | isa => WxSizer, 260 | lazy => 1, 261 | builder => '_build_ed_deploy_sbs', 262 | ); 263 | 264 | has 'ed_revert_sbs' => ( 265 | is => 'ro', 266 | isa => WxSizer, 267 | lazy => 1, 268 | builder => '_build_ed_revert_sbs', 269 | ); 270 | 271 | has 'ed_verify_sbs' => ( 272 | is => 'ro', 273 | isa => WxSizer, 274 | lazy => 1, 275 | builder => '_build_ed_verify_sbs', 276 | ); 277 | 278 | sub BUILD { 279 | my $self = shift; 280 | 281 | $self->panel->Hide; 282 | 283 | $self->sizer->Add( $self->sb_sizer, 1, wxEXPAND | wxALL, 5 ); 284 | $self->sb_sizer->Add( $self->main_fg_sz, 1, wxEXPAND | wxALL, 5 ); 285 | 286 | $self->main_fg_sz->Add( $self->top_sizer, 1, wxEXPAND | wxALL, 5 ); 287 | $self->main_fg_sz->Add( $self->notebook, 1, wxEXPAND | wxALL, 5 ); 288 | 289 | $self->top_sizer->Add($self->collpane, 1, wxEXPAND | wxALL, 5); # 0 prop! 290 | #$self->collpane->GetPane->SetBackgroundColour( Wx::Colour->new('red') ); 291 | 292 | #-- Top form 293 | $self->colpsizer->Add(-1, 15); # vspace 294 | $self->colpsizer->Add($self->form_fg_sz, 1, wxEXPAND | wxLEFT, 0); 295 | 296 | $self->form_fg_sz->Add( $self->lbl_change_id, 0, wxLEFT, 0); 297 | $self->form_fg_sz->Add( $self->txt_change_id, 1, wxEXPAND | wxLEFT, 0); 298 | 299 | $self->form_fg_sz->Add( $self->lbl_name, 0, wxLEFT, 0); 300 | $self->form_fg_sz->Add( $self->txt_name, 1, wxEXPAND | wxLEFT, 0); 301 | 302 | $self->form_fg_sz->Add( $self->lbl_note, 0, wxLEFT, 0); 303 | $self->form_fg_sz->Add( $self->txt_note, 1, wxEXPAND | wxLEFT, 0); 304 | 305 | $self->form_fg_sz->Add( $self->lbl_planned_at, 0, wxLEFT, 0); 306 | $self->form_fg_sz->Add( $self->txt_planned_at, 1, wxEXPAND | wxLEFT, 0); 307 | 308 | $self->form_fg_sz->Add( $self->lbl_planner_name, 0, wxLEFT, 0); 309 | $self->form_fg_sz->Add( $self->txt_planner_name, 1, wxEXPAND | wxLEFT, 0); 310 | 311 | $self->form_fg_sz->Add( $self->lbl_planner_email, 0, wxLEFT, 0); 312 | $self->form_fg_sz->Add( $self->txt_planner_email, 1, wxEXPAND | wxLEFT, 0); 313 | 314 | $self->form_fg_sz->Add( $self->lbl_committed_at, 0, wxLEFT, 0); 315 | $self->form_fg_sz->Add( $self->txt_committed_at, 1, wxEXPAND | wxLEFT, 0); 316 | 317 | $self->form_fg_sz->Add( $self->lbl_committer_name, 0, wxLEFT, 0); 318 | $self->form_fg_sz->Add( $self->txt_committer_name, 1, wxEXPAND | wxLEFT, 0); 319 | 320 | $self->form_fg_sz->Add( $self->lbl_committer_email, 0, wxLEFT, 0); 321 | $self->form_fg_sz->Add( $self->txt_committer_email, 1, wxEXPAND | wxLEFT, 0); 322 | $self->collpane->GetPane->SetSizer($self->colpsizer); 323 | 324 | #-- Notebook on the bottom side for SQL edit 325 | #--- Page Deploy 326 | 327 | $self->notebook->page_deploy->SetSizer( $self->deploy_sz ); 328 | $self->ed_deploy_sbs->Add($self->edit_deploy, 1, wxEXPAND | wxALL, 5 ); 329 | $self->deploy_sz->Add( $self->ed_deploy_sbs, 1, wxEXPAND | wxALL, 5 ); 330 | 331 | #--- Page Revert 332 | 333 | $self->notebook->page_revert->SetSizer( $self->revert_sz ); 334 | $self->ed_revert_sbs->Add($self->edit_revert, 1, wxEXPAND | wxALL, 5 ); 335 | $self->revert_sz->Add( $self->ed_revert_sbs, 1, wxEXPAND | wxALL, 5 ); 336 | 337 | #--- Page Verify 338 | 339 | $self->notebook->page_verify->SetSizer($self->verify_sz); 340 | $self->ed_verify_sbs->Add( $self->edit_verify, 1, wxEXPAND | wxALL, 5 ); 341 | $self->verify_sz->Add( $self->ed_verify_sbs, 1, wxEXPAND | wxALL, 5 ); 342 | 343 | $self->panel->SetSizer( $self->sizer ); 344 | 345 | return $self; 346 | } 347 | 348 | sub _build_panel { 349 | my $self = shift; 350 | 351 | my $panel = Wx::Panel->new( 352 | $self->parent, 353 | -1, 354 | [ -1, -1 ], 355 | [ -1, -1 ], 356 | wxFULL_REPAINT_ON_RESIZE, 357 | 'changePanel', 358 | ); 359 | #$panel->SetBackgroundColour(Wx::Colour->new('red')); 360 | 361 | return $panel; 362 | } 363 | 364 | sub _build_collpane { 365 | my $self = shift; 366 | 367 | my $pane = Wx::CollapsiblePane->new( 368 | $self->panel, 369 | -1, 370 | 'Details', 371 | [-1,-1], 372 | [-1,-1], 373 | wxCP_DEFAULT_STYLE | wxCP_NO_TLW_RESIZE, 374 | ); 375 | 376 | return $pane; 377 | } 378 | 379 | sub _build_sizer { 380 | return Wx::BoxSizer->new(wxHORIZONTAL); 381 | } 382 | 383 | sub _build_top_sizer { 384 | return Wx::BoxSizer->new(wxVERTICAL); 385 | } 386 | 387 | sub _build_colpsizer { 388 | return Wx::BoxSizer->new(wxVERTICAL); 389 | } 390 | 391 | sub _build_main_fg_sz { 392 | my $fgs = Wx::FlexGridSizer->new( 2, 1, 1, 5 ); 393 | $fgs->AddGrowableRow(1); 394 | $fgs->AddGrowableCol(0); 395 | return $fgs; 396 | } 397 | 398 | sub _build_form_fg_sz { 399 | my $fgs = Wx::FlexGridSizer->new( 9, 2, 5, 10 ); 400 | $fgs->AddGrowableRow(1); 401 | $fgs->AddGrowableCol(1); 402 | return $fgs; 403 | } 404 | 405 | sub _build_lbl_change_id { 406 | my $self = shift; 407 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Change Id} ); 408 | } 409 | 410 | sub _build_lbl_name { 411 | my $self = shift; 412 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Name} ); 413 | } 414 | 415 | sub _build_lbl_note { 416 | my $self = shift; 417 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Note} ); 418 | } 419 | 420 | sub _build_lbl_committed_at { 421 | my $self = shift; 422 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Commited at} ); 423 | } 424 | 425 | sub _build_lbl_committer_name { 426 | my $self = shift; 427 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Commiter name} ); 428 | } 429 | 430 | sub _build_lbl_committer_email { 431 | my $self = shift; 432 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Commiter email} ); 433 | } 434 | 435 | sub _build_lbl_planned_at { 436 | my $self = shift; 437 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Planned at} ); 438 | } 439 | 440 | sub _build_lbl_planner_name { 441 | my $self = shift; 442 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Planner name} ); 443 | } 444 | 445 | sub _build_lbl_planner_email { 446 | my $self = shift; 447 | return Wx::StaticText->new( $self->collpane->GetPane, -1, q{Planner email} ); 448 | } 449 | 450 | sub _build_txt_change_id { 451 | my $self = shift; 452 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 453 | } 454 | 455 | sub _build_txt_name { 456 | my $self = shift; 457 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 458 | } 459 | 460 | sub _build_txt_note{ 461 | my $self = shift; 462 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 463 | } 464 | 465 | sub _build_txt_committed_at { 466 | my $self = shift; 467 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 468 | } 469 | 470 | sub _build_txt_committer_name { 471 | my $self = shift; 472 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 473 | } 474 | 475 | sub _build_txt_committer_email { 476 | my $self = shift; 477 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 478 | } 479 | 480 | sub _build_txt_planned_at { 481 | my $self = shift; 482 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 483 | } 484 | 485 | sub _build_txt_planner_name { 486 | my $self = shift; 487 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 488 | } 489 | 490 | sub _build_txt_planner_email { 491 | my $self = shift; 492 | return Wx::TextCtrl->new( $self->collpane->GetPane, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 493 | } 494 | 495 | #-- 496 | 497 | sub _build_deploy_sz { 498 | return Wx::BoxSizer->new(wxHORIZONTAL); 499 | } 500 | 501 | sub _build_verify_sz { 502 | return Wx::BoxSizer->new(wxHORIZONTAL); 503 | } 504 | 505 | sub _build_revert_sz { 506 | return Wx::BoxSizer->new(wxHORIZONTAL); 507 | } 508 | 509 | sub _build_sb_sizer { 510 | my $self = shift; 511 | 512 | return Wx::StaticBoxSizer->new( 513 | Wx::StaticBox->new( $self->panel, -1, ' Change ', ), wxHORIZONTAL ); 514 | } 515 | 516 | sub _build_notebook { 517 | my $self = shift; 518 | 519 | return App::Sqitch::GUI::Wx::Notebook->new( 520 | app => $self->app, 521 | parent => $self->panel, 522 | ancestor => $self, 523 | ); 524 | } 525 | 526 | sub _build_edit_deploy { 527 | my $self = shift; 528 | 529 | return App::Sqitch::GUI::Wx::Editor->new( 530 | app => $self->app, 531 | parent => $self->notebook->page_deploy, 532 | ancestor => $self, 533 | ); 534 | } 535 | 536 | sub _build_edit_revert { 537 | my $self = shift; 538 | 539 | return App::Sqitch::GUI::Wx::Editor->new( 540 | app => $self->app, 541 | parent => $self->notebook->page_revert, 542 | ancestor => $self, 543 | ); 544 | } 545 | 546 | sub _build_edit_verify { 547 | my $self = shift; 548 | 549 | return App::Sqitch::GUI::Wx::Editor->new( 550 | app => $self->app, 551 | parent => $self->notebook->page_verify, 552 | ancestor => $self, 553 | ); 554 | } 555 | 556 | sub _build_ed_deploy_sbs { 557 | my $self = shift; 558 | 559 | return Wx::StaticBoxSizer->new( 560 | Wx::StaticBox->new( 561 | $self->notebook->page_deploy, 562 | -1, ' View | Edit ', 563 | ), 564 | wxHORIZONTAL 565 | ); 566 | } 567 | 568 | sub _build_ed_revert_sbs { 569 | my $self = shift; 570 | 571 | return Wx::StaticBoxSizer->new( 572 | Wx::StaticBox->new( 573 | $self->notebook->page_revert, 574 | -1, ' View | Edit ', 575 | ), 576 | wxHORIZONTAL 577 | ); 578 | } 579 | 580 | sub _build_ed_verify_sbs { 581 | my $self = shift; 582 | 583 | return Wx::StaticBoxSizer->new( 584 | Wx::StaticBox->new( 585 | $self->notebook->page_verify, 586 | -1, ' View | Edit ', 587 | ), 588 | wxHORIZONTAL 589 | ); 590 | } 591 | 592 | sub _set_events { 593 | my ($self, $event) = @_; 594 | 595 | EVT_COLLAPSIBLEPANE_CHANGED $self->parent, $self->collpane, 596 | sub { $self->OnPaneChanged(@_); }; 597 | 598 | return; 599 | } 600 | 601 | sub OnPaneChanged { 602 | my ($self, $frame, $event) = @_; 603 | $frame->Layout(); 604 | } 605 | 606 | sub OnClose { 607 | my ($self, $event) = @_; 608 | } 609 | 610 | =head1 AUTHOR 611 | 612 | Stefan Suciu, C<< >> 613 | 614 | =head1 BUGS 615 | 616 | None known. 617 | 618 | Please report any bugs or feature requests to the author. 619 | 620 | =head1 ACKNOWLEDGMENTS 621 | 622 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 623 | project: 624 | 625 | https://github.com/tmtowtdi/LacunaWaX 626 | 627 | Copyright: Jonathan D. Barton 2012-2013 628 | 629 | Thank you! 630 | 631 | =head1 LICENSE AND COPYRIGHT 632 | 633 | Stefan Suciu 2013 634 | 635 | This program is free software; you can redistribute it and/or modify it 636 | under the terms of either: the GNU General Public License as published 637 | by the Free Software Foundation. 638 | 639 | =cut 640 | 641 | 1; # End of App::Sqitch::GUI::Panel::Change 642 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Panel/Left.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Panel::Left; 2 | 3 | # ABSTRACT: The Left Panel 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | WxPanel 10 | WxSizer 11 | ); 12 | use Wx qw(:allclasses :everything); 13 | use Wx::Event qw(EVT_CLOSE); 14 | 15 | with 'App::Sqitch::GUI::Roles::Element'; 16 | 17 | has 'panel' => ( 18 | is => 'ro', 19 | isa => WxPanel, 20 | lazy => 1, 21 | builder => '_build_panel', 22 | ); 23 | 24 | has 'sizer' => ( 25 | is => 'ro', 26 | isa => WxSizer, 27 | lazy => 1, 28 | builder => '_build_sizer', 29 | ); 30 | 31 | sub BUILD { 32 | my $self = shift; 33 | 34 | #- The main panel 35 | 36 | $self->panel->Show(0); 37 | $self->panel->SetSizer( $self->sizer ); 38 | $self->panel->Show(1); 39 | 40 | return $self; 41 | } 42 | 43 | sub _build_panel { 44 | my $self = shift; 45 | 46 | my $panel = Wx::Panel->new( 47 | $self->parent, 48 | -1, 49 | [ -1, -1 ], 50 | [ -1, -1 ], 51 | wxFULL_REPAINT_ON_RESIZE, 52 | 'mainPanel', 53 | ); 54 | #$panel->SetBackgroundColour(Wx::Colour->new('light green')); 55 | 56 | return $panel; 57 | } 58 | 59 | sub _build_sizer { 60 | return Wx::BoxSizer->new(wxVERTICAL); 61 | } 62 | 63 | sub _set_events { } 64 | 65 | =head1 AUTHOR 66 | 67 | Stefan Suciu, C<< >> 68 | 69 | =head1 BUGS 70 | 71 | None known. 72 | 73 | Please report any bugs or feature requests to the author. 74 | 75 | =head1 ACKNOWLEDGMENTS 76 | 77 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 78 | project: 79 | 80 | https://github.com/tmtowtdi/LacunaWaX 81 | 82 | Copyright: Jonathan D. Barton 2012-2013 83 | 84 | Thank you! 85 | 86 | =head1 LICENSE AND COPYRIGHT 87 | 88 | Stefan Suciu 2013 89 | 90 | This program is free software; you can redistribute it and/or modify it 91 | under the terms of either: the GNU General Public License as published 92 | by the Free Software Foundation. 93 | 94 | =cut 95 | 96 | 1; # End of App::Sqitch::GUI::View::Panel::Left 97 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Panel/Plan.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Panel::Plan; 2 | 3 | # ABSTRACT: The Plan Panel 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | WxButton 10 | WxPanel 11 | WxSizer 12 | SqitchGUIWxListctrl 13 | SqitchGUIModelListDataTable 14 | ); 15 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 16 | use Wx qw(:allclasses :everything); 17 | use Wx::Event qw(EVT_CLOSE); 18 | 19 | use App::Sqitch::GUI::Wx::Listctrl; 20 | 21 | with 'App::Sqitch::GUI::Roles::Element'; 22 | 23 | has 'panel' => ( 24 | is => 'ro', 25 | isa => WxPanel, 26 | lazy => 1, 27 | builder => '_build_panel', 28 | ); 29 | 30 | has 'sizer' => ( 31 | is => 'ro', 32 | isa => WxSizer, 33 | lazy => 1, 34 | builder => '_build_sizer', 35 | ); 36 | 37 | has 'btn_sizer' => ( 38 | is => 'ro', 39 | isa => WxSizer, 40 | lazy => 1, 41 | builder => '_build_btn_sizer', 42 | ); 43 | 44 | has 'sb_sizer' => ( 45 | is => 'ro', 46 | isa => WxSizer, 47 | lazy => 1, 48 | builder => '_build_sb_sizer', 49 | ); 50 | 51 | has 'main_fg_sz' => ( 52 | is => 'ro', 53 | isa => WxSizer, 54 | lazy => 1, 55 | builder => '_build_main_fg_sz', 56 | ); 57 | 58 | has 'list_fg_sz' => ( 59 | is => 'ro', 60 | isa => WxSizer, 61 | lazy => 1, 62 | builder => '_build_list_fg_sz', 63 | ); 64 | 65 | has 'list_ctrl' => ( 66 | is => 'ro', 67 | isa => SqitchGUIWxListctrl, 68 | lazy => 1, 69 | builder => '_build_list_ctrl', 70 | ); 71 | 72 | has 'btn_load' => ( 73 | is => 'ro', 74 | isa => WxButton, 75 | lazy => 1, 76 | builder => '_build_btn_load', 77 | ); 78 | 79 | sub _build_list_ctrl { 80 | my $self = shift; 81 | my $list_ctrl = App::Sqitch::GUI::Wx::Listctrl->new( 82 | app => $self->app, 83 | parent => $self->panel, 84 | list_data => $self->app->model->plan_list_data, 85 | meta_data => $self->app->model->plan_list_meta_data, 86 | ); 87 | return $list_ctrl; 88 | } 89 | 90 | sub BUILD { 91 | my $self = shift; 92 | 93 | $self->panel->Hide; 94 | 95 | $self->sizer->Add( $self->sb_sizer, 1, wxEXPAND | wxALL, 5 ); 96 | 97 | $self->sb_sizer->Add( $self->main_fg_sz, 1, wxEXPAND | wxALL, 5 ); 98 | 99 | $self->main_fg_sz->Add( $self->list_fg_sz, 1, wxEXPAND | wxALL, 5 ); 100 | 101 | #-- List 102 | 103 | $self->list_fg_sz->Add( $self->list_ctrl, 1, wxEXPAND, 3 ); 104 | 105 | $self->panel->SetSizer($self->sizer); 106 | 107 | #-- Button 108 | 109 | $self->btn_sizer->Add( $self->btn_load, 1, wxLEFT | wxRIGHT | wxEXPAND, 25 ); 110 | $self->main_fg_sz->Add( $self->btn_sizer, 1, wxALIGN_CENTRE); 111 | 112 | return $self; 113 | } 114 | 115 | sub _build_panel { 116 | my $self = shift; 117 | 118 | my $panel = Wx::Panel->new( 119 | $self->parent, 120 | -1, 121 | [ -1, -1 ], 122 | [ -1, -1 ], 123 | wxFULL_REPAINT_ON_RESIZE, 124 | 'projectPanel', 125 | ); 126 | #$panel->SetBackgroundColour( Wx::Colour->new('blue') ); 127 | 128 | return $panel; 129 | } 130 | 131 | sub _build_sizer { 132 | return Wx::BoxSizer->new(wxHORIZONTAL); 133 | } 134 | 135 | sub _build_main_fg_sz { 136 | my $fgs = Wx::FlexGridSizer->new( 1, 1, 1, 5 ); 137 | $fgs->AddGrowableRow(0); 138 | $fgs->AddGrowableCol(0); 139 | return $fgs; 140 | } 141 | 142 | sub _build_list_fg_sz { 143 | my $fgs = Wx::FlexGridSizer->new( 2, 1, 1, 5 ); 144 | $fgs->AddGrowableRow(0); 145 | $fgs->AddGrowableCol(0); 146 | return $fgs; 147 | } 148 | 149 | sub _build_sb_sizer { 150 | my $self = shift; 151 | 152 | return Wx::StaticBoxSizer->new( 153 | Wx::StaticBox->new( $self->panel, -1, __ 'Plan ', ), wxVERTICAL ); 154 | } 155 | 156 | sub _build_btn_sizer { 157 | return Wx::BoxSizer->new(wxHORIZONTAL); 158 | } 159 | 160 | #-- Buttons 161 | 162 | sub _build_btn_load { 163 | my $self = shift; 164 | 165 | my $button = Wx::Button->new( 166 | $self->panel, 167 | -1, 168 | __ 'Load', 169 | [ -1, -1 ], 170 | [ -1, -1 ], 171 | ); 172 | $button->Enable(1); 173 | 174 | return $button; 175 | } 176 | 177 | sub _set_events { } 178 | 179 | sub OnClose { 180 | my ($self, $event) = @_; 181 | } 182 | 183 | =head1 AUTHOR 184 | 185 | Stefan Suciu, C<< >> 186 | 187 | =head1 BUGS 188 | 189 | None known. 190 | 191 | Please report any bugs or feature requests to the author. 192 | 193 | =head1 ACKNOWLEDGMENTS 194 | 195 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 196 | project: 197 | 198 | https://github.com/tmtowtdi/LacunaWaX 199 | 200 | Copyright: Jonathan D. Barton 2012-2013 201 | 202 | Thank you! 203 | 204 | =head1 LICENSE AND COPYRIGHT 205 | 206 | Stefan Suciu 2013 207 | 208 | This program is free software; you can redistribute it and/or modify it 209 | under the terms of either: the GNU General Public License as published 210 | by the Free Software Foundation. 211 | 212 | =cut 213 | 214 | 1; # End of App::Sqitch::GUI::Panel::Plan 215 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Panel/Project.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Panel::Project; 2 | 3 | # ABSTRACT: The Project Panel 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | WxButton 10 | WxPanel 11 | WxSizer 12 | WxStaticLine 13 | WxTextCtrl 14 | WxStaticText 15 | SqitchGUIWxListctrl 16 | ); 17 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 18 | use Wx qw(:allclasses :everything); 19 | use Wx::Event qw(EVT_CLOSE); 20 | 21 | use App::Sqitch::GUI::Wx::Listctrl; 22 | 23 | with qw(App::Sqitch::GUI::Roles::Element 24 | App::Sqitch::GUI::Roles::Panel); 25 | 26 | has 'panel' => ( 27 | is => 'ro', 28 | isa => WxPanel, 29 | lazy => 1, 30 | builder => '_build_panel', 31 | ); 32 | 33 | has 'sizer' => ( 34 | is => 'ro', 35 | isa => WxSizer, 36 | lazy => 1, 37 | builder => '_build_sizer', 38 | ); 39 | 40 | has 'btn_sizer' => ( 41 | is => 'ro', 42 | isa => WxSizer, 43 | lazy => 1, 44 | builder => '_build_btn_sizer', 45 | ); 46 | 47 | has 'sb_sizer' => ( 48 | is => 'ro', 49 | isa => WxSizer, 50 | lazy => 1, 51 | builder => '_build_sb_sizer', 52 | ); 53 | 54 | has 'main_fg_sz' => ( 55 | is => 'ro', 56 | isa => WxSizer, 57 | lazy => 1, 58 | builder => '_build_main_fg_sz', 59 | ); 60 | 61 | has 'list_fg_sz' => ( 62 | is => 'ro', 63 | isa => WxSizer, 64 | lazy => 1, 65 | builder => '_build_list_fg_sz', 66 | ); 67 | 68 | has 'form_fg_sz' => ( 69 | is => 'ro', 70 | isa => WxSizer, 71 | lazy => 1, 72 | builder => '_build_form_fg_sz', 73 | ); 74 | 75 | has 'subform1_fg_sz' => ( 76 | is => 'ro', 77 | isa => WxSizer, 78 | lazy => 1, 79 | builder => '_build_subform1_fg_sz', 80 | ); 81 | 82 | has 'subform2_fg_sz' => ( 83 | is => 'ro', 84 | isa => WxSizer, 85 | lazy => 1, 86 | builder => '_build_subform2_fg_sz', 87 | ); 88 | 89 | has 'list_ctrl' => ( 90 | is => 'ro', 91 | isa => SqitchGUIWxListctrl, 92 | lazy => 1, 93 | builder => '_build_list_ctrl', 94 | ); 95 | 96 | has 'h_line1' => ( 97 | is => 'ro', 98 | isa => WxStaticLine, 99 | lazy => 1, 100 | builder => '_build_h_line1', 101 | ); 102 | 103 | has 'btn_load' => ( 104 | is => 'ro', 105 | isa => WxButton, 106 | lazy => 1, 107 | builder => '_build_btn_load', 108 | ); 109 | 110 | has 'btn_init' => ( 111 | is => 'ro', 112 | isa => WxButton, 113 | lazy => 1, 114 | builder => '_build_btn_init', 115 | ); 116 | 117 | has 'btn_default' => ( 118 | is => 'ro', 119 | isa => WxButton, 120 | lazy => 1, 121 | builder => '_build_btn_default', 122 | ); 123 | 124 | has 'lbl_project' => ( 125 | is => 'ro', 126 | isa => WxStaticText, 127 | lazy => 1, 128 | builder => '_build_lbl_project', 129 | ); 130 | 131 | has 'lbl_database' => ( 132 | is => 'ro', 133 | isa => WxStaticText, 134 | lazy => 1, 135 | builder => '_build_lbl_database', 136 | ); 137 | 138 | has 'lbl_user' => ( 139 | is => 'ro', 140 | isa => WxStaticText, 141 | lazy => 1, 142 | builder => '_build_lbl_user', 143 | ); 144 | 145 | has 'lbl_uri' => ( 146 | is => 'ro', 147 | isa => WxStaticText, 148 | lazy => 1, 149 | builder => '_build_lbl_uri', 150 | ); 151 | 152 | has 'lbl_created_at' => ( 153 | is => 'ro', 154 | isa => WxStaticText, 155 | lazy => 1, 156 | builder => '_build_lbl_created_at', 157 | ); 158 | 159 | has 'lbl_creator_name' => ( 160 | is => 'ro', 161 | isa => WxStaticText, 162 | lazy => 1, 163 | builder => '_build_lbl_creator_name', 164 | ); 165 | 166 | has 'lbl_creator_email' => ( 167 | is => 'ro', 168 | isa => WxStaticText, 169 | lazy => 1, 170 | builder => '_build_lbl_creator_email', 171 | ); 172 | 173 | has 'lbl_path' => ( 174 | is => 'ro', 175 | isa => WxStaticText, 176 | lazy => 1, 177 | builder => '_build_lbl_path', 178 | ); 179 | 180 | has 'lbl_engine' => ( 181 | is => 'ro', 182 | isa => WxStaticText, 183 | lazy => 1, 184 | builder => '_build_lbl_engine', 185 | ); 186 | 187 | has 'txt_project' => ( 188 | is => 'ro', 189 | isa => WxTextCtrl, 190 | lazy => 1, 191 | builder => '_build_txt_project', 192 | ); 193 | 194 | has 'txt_database' => ( 195 | is => 'ro', 196 | isa => WxTextCtrl, 197 | lazy => 1, 198 | builder => '_build_txt_database', 199 | ); 200 | 201 | has 'txt_user' => ( 202 | is => 'ro', 203 | isa => WxTextCtrl, 204 | lazy => 1, 205 | builder => '_build_txt_user', 206 | ); 207 | 208 | has 'txt_uri' => ( 209 | is => 'ro', 210 | isa => WxTextCtrl, 211 | lazy => 1, 212 | builder => '_build_txt_uri', 213 | ); 214 | 215 | has 'txt_created_at' => ( 216 | is => 'ro', 217 | isa => WxTextCtrl, 218 | lazy => 1, 219 | builder => '_build_txt_created_at', 220 | ); 221 | 222 | has 'txt_creator_name' => ( 223 | is => 'ro', 224 | isa => WxTextCtrl, 225 | lazy => 1, 226 | builder => '_build_txt_creator_name', 227 | ); 228 | 229 | has 'txt_creator_email' => ( 230 | is => 'ro', 231 | isa => WxTextCtrl, 232 | lazy => 1, 233 | builder => '_build_txt_creator_email', 234 | ); 235 | 236 | has 'txt_path' => ( 237 | is => 'ro', 238 | isa => WxTextCtrl, 239 | lazy => 1, 240 | builder => '_build_txt_path', 241 | ); 242 | 243 | has 'txt_engine' => ( 244 | is => 'ro', 245 | isa => WxTextCtrl, 246 | lazy => 1, 247 | builder => '_build_txt_engine', 248 | ); 249 | 250 | sub BUILD { 251 | my $self = shift; 252 | 253 | $self->panel->Hide; 254 | 255 | $self->sizer->Add( $self->sb_sizer, 1, wxEXPAND | wxALL, 5 ); 256 | 257 | $self->sb_sizer->Add( $self->main_fg_sz, 1, wxEXPAND | wxALL, 5 ); 258 | 259 | $self->main_fg_sz->Add( $self->form_fg_sz, 1, wxEXPAND | wxALL, 5 ); 260 | $self->main_fg_sz->Add( $self->list_fg_sz, 1, wxEXPAND | wxALL, 5 ); 261 | 262 | #-- Top form 263 | 264 | $self->form_fg_sz->Add( $self->lbl_project, 0, wxLEFT, 5 ); 265 | $self->subform1_fg_sz->Add( $self->txt_project, 1, wxLEFT, 0 ); 266 | $self->subform1_fg_sz->Add( $self->lbl_engine, 0, wxLEFT, 50 ); 267 | $self->subform1_fg_sz->Add( $self->txt_engine, 0, wxLEFT, 20 ); 268 | $self->form_fg_sz->Add( $self->subform1_fg_sz, 1, wxEXPAND | wxLEFT, 0 ); 269 | 270 | $self->form_fg_sz->Add( $self->lbl_database, 0, wxLEFT, 5 ); 271 | $self->subform2_fg_sz->Add( $self->txt_database, 1, wxLEFT, 0 ); 272 | $self->subform2_fg_sz->Add( $self->lbl_user, 0, wxLEFT, 20 ); 273 | $self->subform2_fg_sz->Add( $self->txt_user, 1, wxEXPAND | wxLEFT, 20 ); 274 | $self->form_fg_sz->Add( $self->subform2_fg_sz, 1, wxEXPAND | wxLEFT, 0 ); 275 | 276 | $self->form_fg_sz->Add( $self->lbl_path, 0, wxLEFT, 5 ); 277 | $self->form_fg_sz->Add( $self->txt_path, 1, wxEXPAND | wxLEFT, 0 ); 278 | 279 | $self->form_fg_sz->Add( $self->lbl_uri, 0, wxLEFT, 5 ); 280 | $self->form_fg_sz->Add( $self->txt_uri, 1, wxEXPAND | wxLEFT, 0 ); 281 | 282 | $self->form_fg_sz->Add( $self->lbl_created_at, 0, wxLEFT, 5 ); 283 | $self->form_fg_sz->Add( $self->txt_created_at, 1, wxEXPAND | wxLEFT, 0 ); 284 | 285 | $self->form_fg_sz->Add( $self->lbl_creator_name, 0, wxLEFT, 5 ); 286 | $self->form_fg_sz->Add( $self->txt_creator_name, 1, wxEXPAND | wxLEFT, 0 ); 287 | 288 | $self->form_fg_sz->Add( $self->lbl_creator_email, 0, wxLEFT, 5 ); 289 | $self->form_fg_sz->Add( $self->txt_creator_email, 1, wxEXPAND | wxLEFT, 0 ); 290 | 291 | #-- List and buttons 292 | 293 | $self->list_fg_sz->Add( $self->list_ctrl, 1, wxEXPAND, 3 ); 294 | 295 | $self->list_fg_sz->Add( $self->h_line1, 1, wxEXPAND | wxTOP | wxBOTTOM, 296 | 10 ); 297 | $self->btn_sizer->Add( $self->btn_load, 1, wxLEFT | wxRIGHT | wxEXPAND, 298 | 25 ); 299 | $self->btn_sizer->Add( $self->btn_init, 1, wxLEFT | wxRIGHT | wxEXPAND, 300 | 25 ); 301 | $self->btn_sizer->Add( $self->btn_default, 1, 302 | wxLEFT | wxRIGHT | wxEXPAND, 25 ); 303 | 304 | $self->list_fg_sz->Add( $self->btn_sizer, 1, wxALIGN_CENTRE); 305 | 306 | $self->panel->SetSizer($self->sizer); 307 | 308 | return $self; 309 | } 310 | 311 | sub _build_panel { 312 | my $self = shift; 313 | 314 | my $panel = Wx::Panel->new( 315 | $self->parent, 316 | -1, 317 | [ -1, -1 ], 318 | [ -1, -1 ], 319 | wxFULL_REPAINT_ON_RESIZE, 320 | 'projectPanel', 321 | ); 322 | #$panel->SetBackgroundColour( Wx::Colour->new('green') ); 323 | 324 | return $panel; 325 | } 326 | 327 | sub _build_sizer { 328 | return Wx::BoxSizer->new(wxHORIZONTAL); 329 | } 330 | 331 | sub _build_btn_sizer { 332 | return Wx::BoxSizer->new(wxHORIZONTAL); 333 | } 334 | 335 | sub _build_main_fg_sz { 336 | my $fgs = Wx::FlexGridSizer->new( 2, 1, 1, 5 ); 337 | $fgs->AddGrowableRow(1); 338 | $fgs->AddGrowableCol(0); 339 | return $fgs; 340 | } 341 | 342 | #- Form 343 | 344 | sub _build_form_fg_sz { 345 | my $fgs = Wx::FlexGridSizer->new( 7, 2, 6, 10 ); 346 | $fgs->AddGrowableCol(1); 347 | return $fgs; 348 | } 349 | 350 | sub _build_subform1_fg_sz { 351 | my $fgs = Wx::FlexGridSizer->new( 1, 3, 0, 0 ); 352 | $fgs->AddGrowableCol(0); 353 | return $fgs; 354 | } 355 | 356 | sub _build_subform2_fg_sz { 357 | my $fgs = Wx::FlexGridSizer->new( 1, 3, 0, 0 ); 358 | $fgs->AddGrowableCol(0); 359 | return $fgs; 360 | } 361 | 362 | #-- Labels 363 | 364 | sub _build_lbl_project { 365 | my $self = shift; 366 | return Wx::StaticText->new( $self->panel, -1, __ 'Project' ); 367 | } 368 | 369 | sub _build_lbl_database { 370 | my $self = shift; 371 | return Wx::StaticText->new( $self->panel, -1, __ 'Database' ); 372 | } 373 | 374 | sub _build_lbl_user { 375 | my $self = shift; 376 | return Wx::StaticText->new( $self->panel, -1, __ 'User' ); 377 | } 378 | 379 | sub _build_lbl_uri { 380 | my $self = shift; 381 | return Wx::StaticText->new( $self->panel, -1, __ 'URI' ); 382 | } 383 | 384 | sub _build_lbl_created_at { 385 | my $self = shift; 386 | return Wx::StaticText->new( $self->panel, -1, __ 'Created at' ); 387 | } 388 | 389 | sub _build_lbl_creator_name { 390 | my $self = shift; 391 | return Wx::StaticText->new( $self->panel, -1, __ 'Creator name' ); 392 | } 393 | 394 | sub _build_lbl_creator_email { 395 | my $self = shift; 396 | return Wx::StaticText->new( $self->panel, -1, __ 'Creator email' ); 397 | } 398 | 399 | sub _build_lbl_engine { 400 | my $self = shift; 401 | return Wx::StaticText->new( $self->panel, -1, __ 'Engine' ); 402 | } 403 | 404 | sub _build_lbl_path { 405 | my $self = shift; 406 | return Wx::StaticText->new( $self->panel, -1, __ 'Repository' ); 407 | } 408 | 409 | #-- Entry 410 | 411 | sub _build_txt_project { 412 | my $self = shift; 413 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 400, -1 ] ); 414 | } 415 | 416 | sub _build_txt_database { 417 | my $self = shift; 418 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 400, -1 ] ); 419 | } 420 | 421 | sub _build_txt_user { 422 | my $self = shift; 423 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 100, -1 ] ); 424 | } 425 | 426 | sub _build_txt_uri { 427 | my $self = shift; 428 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 429 | } 430 | 431 | sub _build_txt_created_at { 432 | my $self = shift; 433 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 434 | } 435 | 436 | sub _build_txt_creator_name { 437 | my $self = shift; 438 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 439 | } 440 | 441 | sub _build_txt_creator_email { 442 | my $self = shift; 443 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 444 | } 445 | 446 | sub _build_txt_path { 447 | my $self = shift; 448 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 170, -1 ] ); 449 | } 450 | 451 | sub _build_txt_engine { 452 | my $self = shift; 453 | return Wx::TextCtrl->new( $self->panel, -1, q{}, [ -1, -1 ], [ 100, -1 ] ); 454 | } 455 | 456 | #- List and buttons 457 | 458 | sub _build_list_fg_sz { 459 | my $fgs = Wx::FlexGridSizer->new( 2, 1, 1, 5 ); 460 | $fgs->AddGrowableRow(0); 461 | $fgs->AddGrowableCol(0); 462 | return $fgs; 463 | } 464 | 465 | sub _build_sb_sizer { 466 | my $self = shift; 467 | 468 | return Wx::StaticBoxSizer->new( 469 | Wx::StaticBox->new( $self->panel, -1, __ 'Project', ), wxVERTICAL ); 470 | } 471 | 472 | #-- Lines 473 | 474 | sub _build_h_line1 { 475 | my $self = shift; 476 | return Wx::StaticLine->new( 477 | $self->panel, 478 | -1, 479 | [ -1, -1 ], 480 | [ -1, -1 ], 481 | ); 482 | } 483 | 484 | sub _build_btn_load { 485 | my $self = shift; 486 | my $button = Wx::Button->new( 487 | $self->panel, 488 | -1, 489 | __ 'Load', 490 | [ -1, -1 ], 491 | [ -1, -1 ], 492 | ); 493 | $button->Enable(0); 494 | return $button; 495 | } 496 | 497 | sub _build_btn_init { 498 | my $self = shift; 499 | my $button = Wx::Button->new( 500 | $self->panel, 501 | -1, 502 | __ 'Initialize', 503 | [ -1, -1 ], 504 | [ -1, -1 ], 505 | ); 506 | $button->Enable(0); 507 | return $button; 508 | } 509 | 510 | sub _build_btn_default { 511 | my $self = shift; 512 | my $button = Wx::Button->new( 513 | $self->panel, 514 | -1, 515 | __ 'Default', 516 | [ -1, -1 ], 517 | [ -1, -1 ], 518 | ); 519 | $button->Enable(0); 520 | return $button; 521 | } 522 | 523 | sub _build_list_ctrl { 524 | my $self = shift; 525 | my $list_ctrl = App::Sqitch::GUI::Wx::Listctrl->new( 526 | app => $self->app, 527 | parent => $self->panel, 528 | list_data => $self->app->model->project_list_data, 529 | meta_data => $self->app->model->project_list_meta_data, 530 | ); 531 | return $list_ctrl; 532 | } 533 | 534 | sub _set_events { 535 | my ($self, $event) = @_; 536 | return; 537 | } 538 | 539 | sub OnClose { 540 | my ($self, $event) = @_; 541 | } 542 | 543 | =head1 AUTHOR 544 | 545 | Stefan Suciu, C<< >> 546 | 547 | =head1 BUGS 548 | 549 | None known. 550 | 551 | Please report any bugs or feature requests to the author. 552 | 553 | =head1 ACKNOWLEDGMENTS 554 | 555 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 556 | project: 557 | 558 | https://github.com/tmtowtdi/LacunaWaX 559 | 560 | Copyright: Jonathan D. Barton 2012-2013 561 | 562 | Thank you! 563 | 564 | =head1 LICENSE AND COPYRIGHT 565 | 566 | Stefan Suciu 2013 567 | 568 | This program is free software; you can redistribute it and/or modify it 569 | under the terms of either: the GNU General Public License as published 570 | by the Free Software Foundation. 571 | 572 | =cut 573 | 574 | 1; # End of App::Sqitch::GUI::Panel::Project 575 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Panel/Right.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Panel::Right; 2 | 3 | # ABSTRACT: The Right Panel 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | WxPanel 10 | WxSizer 11 | WxButton 12 | WxRadioButton 13 | ); 14 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 15 | use Wx qw(:allclasses :everything); 16 | use Wx::Event qw; 17 | 18 | with 'App::Sqitch::GUI::Roles::Element'; 19 | 20 | has 'panel' => ( 21 | is => 'ro', 22 | isa => WxPanel, 23 | lazy => 1, 24 | builder => '_build_panel', 25 | ); 26 | 27 | has 'sizer' => ( 28 | is => 'ro', 29 | isa => WxSizer, 30 | lazy => 1, 31 | builder => '_build_sizer', 32 | ); 33 | 34 | has 'panel_sbs' => ( 35 | is => 'ro', 36 | isa => WxSizer, 37 | lazy => 1, 38 | builder => '_build_panel_sbs', 39 | ); 40 | 41 | has 'panel_fgs' => ( 42 | is => 'ro', 43 | isa => WxSizer, 44 | lazy => 1, 45 | builder => '_build_panel_fgs', 46 | ); 47 | 48 | has 'commands_sbs' => ( 49 | is => 'ro', 50 | isa => WxSizer, 51 | lazy => 1, 52 | builder => '_build_commands_sbs', 53 | ); 54 | 55 | has 'commands_fgs' => ( 56 | is => 'ro', 57 | isa => WxSizer, 58 | lazy => 1, 59 | builder => '_build_commands_fgs', 60 | ); 61 | 62 | has 'sizer_cmdtop' => ( 63 | is => 'ro', 64 | isa => WxSizer, 65 | lazy => 1, 66 | builder => '_build_sizer_cmdtop', 67 | ); 68 | 69 | has 'sizer_cmdbot' => ( 70 | is => 'ro', 71 | isa => WxSizer, 72 | lazy => 1, 73 | builder => '_build_sizer_cmdbot', 74 | ); 75 | 76 | has 'btn_status' => ( 77 | is => 'ro', 78 | isa => WxButton, 79 | lazy => 1, 80 | builder => '_build_btn_status', 81 | ); 82 | 83 | has 'btn_add' => ( 84 | is => 'ro', 85 | isa => WxButton, 86 | lazy => 1, 87 | builder => '_build_btn_add', 88 | ); 89 | 90 | has 'btn_deploy' => ( 91 | is => 'ro', 92 | isa => WxButton, 93 | lazy => 1, 94 | builder => '_build_btn_deploy', 95 | ); 96 | 97 | has 'btn_revert' => ( 98 | is => 'ro', 99 | isa => WxButton, 100 | lazy => 1, 101 | builder => '_build_btn_revert', 102 | ); 103 | 104 | has 'btn_verify' => ( 105 | is => 'ro', 106 | isa => WxButton, 107 | lazy => 1, 108 | builder => '_build_btn_verify', 109 | ); 110 | 111 | has 'btn_log' => ( 112 | is => 'ro', 113 | isa => WxButton, 114 | lazy => 1, 115 | builder => '_build_btn_log', 116 | ); 117 | 118 | has 'btn_project' => ( 119 | is => 'ro', 120 | isa => WxButton, 121 | lazy => 1, 122 | builder => '_build_btn_project', 123 | ); 124 | 125 | has 'btn_project_sel' => ( 126 | is => 'ro', 127 | isa => WxRadioButton, 128 | lazy => 1, 129 | builder => '_build_btn_project_sel', 130 | ); 131 | 132 | has 'btn_change' => ( 133 | is => 'ro', 134 | isa => WxButton, 135 | lazy => 1, 136 | builder => '_build_btn_change', 137 | ); 138 | 139 | has 'btn_change_sel' => ( 140 | is => 'ro', 141 | isa => WxRadioButton, 142 | lazy => 1, 143 | builder => '_build_btn_change_sel', 144 | ); 145 | 146 | has 'btn_plan' => ( 147 | is => 'ro', 148 | isa => WxButton, 149 | lazy => 1, 150 | builder => '_build_btn_plan', 151 | ); 152 | 153 | has 'btn_plan_sel' => ( 154 | is => 'ro', 155 | isa => WxRadioButton, 156 | lazy => 1, 157 | builder => '_build_btn_plan_sel', 158 | ); 159 | 160 | sub BUILD { 161 | my $self = shift; 162 | 163 | #- The main panel 164 | 165 | $self->panel->Show(0); 166 | $self->panel->SetSizer( $self->sizer ); 167 | 168 | #--- Panels 169 | 170 | $self->sizer->Add( $self->panel_sbs, 0, wxEXPAND | wxALL, 5 ); 171 | $self->panel_sbs->Add( $self->panel_fgs, 0, wxEXPAND | wxALL, 5 ); 172 | $self->panel_fgs->Add( $self->btn_project_sel, 0, wxEXPAND, 5 ); 173 | $self->panel_fgs->Add( $self->btn_project, 0, wxEXPAND, 5 ); 174 | $self->panel_fgs->Add( $self->btn_plan_sel, 0, wxEXPAND, 5 ); 175 | $self->panel_fgs->Add( $self->btn_plan, 0, wxEXPAND, 5 ); 176 | $self->panel_fgs->Add( $self->btn_change_sel, 0, wxEXPAND, 5 ); 177 | $self->panel_fgs->Add( $self->btn_change, 0, wxEXPAND, 5 ); 178 | 179 | #--- Commands 180 | 181 | $self->sizer->Add( $self->commands_sbs, 1, wxEXPAND | wxALL, 5 ); 182 | $self->commands_sbs->Add( $self->sizer_cmdtop, 1, wxEXPAND | wxALL, 5 ); 183 | $self->commands_sbs->Add( $self->sizer_cmdbot, 0, wxEXPAND | wxALL, 5 ); 184 | $self->sizer_cmdtop->Add( $self->commands_fgs, 1, wxEXPAND | wxALL, 5 ); 185 | $self->commands_fgs->Add( $self->btn_status, 1, wxEXPAND, 0 ); 186 | $self->commands_fgs->Add( $self->btn_add, 1, wxEXPAND, 0 ); 187 | $self->commands_fgs->Add( $self->btn_deploy, 1, wxEXPAND, 0 ); 188 | $self->commands_fgs->Add( $self->btn_revert, 1, wxEXPAND, 0 ); 189 | $self->commands_fgs->Add( $self->btn_verify, 1, wxEXPAND, 0 ); 190 | $self->commands_fgs->Add( $self->btn_log, 1, wxEXPAND, 0 ); 191 | 192 | $self->panel->Show(1); 193 | 194 | return $self; 195 | } 196 | 197 | sub _build_panel { 198 | my $self = shift; 199 | 200 | my $panel = Wx::Panel->new( 201 | $self->parent, 202 | -1, 203 | [ -1, -1 ], 204 | [ -1, -1 ], 205 | wxFULL_REPAINT_ON_RESIZE, 206 | 'mainPanel', 207 | ); 208 | #$panel->SetBackgroundColour(Wx::Colour->new('green')); 209 | 210 | return $panel; 211 | } 212 | 213 | sub _build_sizer { 214 | return Wx::BoxSizer->new(wxVERTICAL); 215 | } 216 | 217 | sub _build_commands_sbs { 218 | my $self = shift; 219 | 220 | return Wx::StaticBoxSizer->new( 221 | Wx::StaticBox->new( $self->panel, -1, ' Commands ', ), 222 | wxVERTICAL ); 223 | } 224 | 225 | sub _build_panel_sbs { 226 | my $self = shift; 227 | 228 | return Wx::StaticBoxSizer->new( 229 | Wx::StaticBox->new( $self->panel, -1, ' Select Panel ', ), 230 | wxVERTICAL ); 231 | } 232 | 233 | sub _build_commands_fgs { 234 | my $fgsz = Wx::FlexGridSizer->new( 10, 0, 5, 0 ); # 10 rows for buttons 235 | $fgsz->AddGrowableCol( 0, 1 ); 236 | return $fgsz; 237 | } 238 | 239 | sub _build_sizer_cmdtop { 240 | return Wx::BoxSizer->new(wxVERTICAL); 241 | } 242 | 243 | sub _build_sizer_cmdbot { 244 | return Wx::BoxSizer->new(wxHORIZONTAL); 245 | } 246 | 247 | sub _build_panel_fgs { 248 | return Wx::FlexGridSizer->new( 3, 2, 5, 0 ); # 3 rows for buttons 249 | } 250 | 251 | sub _build_btn_status { 252 | my $self = shift; 253 | 254 | return Wx::Button->new( 255 | $self->panel, 256 | -1, 257 | q{Status}, 258 | [ -1, -1 ], 259 | [ -1, -1 ], 260 | ); 261 | } 262 | 263 | sub _build_btn_add { 264 | my $self = shift; 265 | 266 | return Wx::Button->new( 267 | $self->panel, 268 | -1, 269 | q{Add}, 270 | [ -1, -1 ], 271 | [ -1, -1 ], 272 | ); 273 | } 274 | 275 | sub _build_btn_deploy { 276 | my $self = shift; 277 | 278 | return Wx::Button->new( 279 | $self->panel, 280 | -1, 281 | q{Deploy}, 282 | [ -1, -1 ], 283 | [ -1, -1 ], 284 | ); 285 | } 286 | 287 | sub _build_btn_revert { 288 | my $self = shift; 289 | 290 | return Wx::Button->new( 291 | $self->panel, 292 | -1, 293 | q{Revert}, 294 | [ -1, -1 ], 295 | [ -1, -1 ], 296 | ); 297 | } 298 | 299 | sub _build_btn_verify { 300 | my $self = shift; 301 | 302 | return Wx::Button->new( 303 | $self->panel, 304 | -1, 305 | q{Verify}, 306 | [ -1, -1 ], 307 | [ -1, -1 ], 308 | ); 309 | } 310 | 311 | sub _build_btn_log { 312 | my $self = shift; 313 | 314 | return Wx::Button->new( 315 | $self->panel, 316 | -1, 317 | q{Log}, 318 | [ -1, -1 ], 319 | [ -1, -1 ], 320 | ); 321 | } 322 | 323 | sub _build_btn_change_sel { 324 | my $self = shift; 325 | 326 | return Wx::RadioButton->new( 327 | $self->panel, 328 | -1, 329 | q{« }, 330 | [-1, -1], 331 | [-1, -1], 332 | ); 333 | } 334 | 335 | sub _build_btn_change { 336 | my $self = shift; 337 | 338 | return Wx::Button->new( 339 | $self->panel, 340 | -1, 341 | q{Change}, 342 | [ -1, -1 ], 343 | [ -1, -1 ], 344 | ); 345 | } 346 | 347 | sub _build_btn_project_sel { 348 | my $self = shift; 349 | 350 | return Wx::RadioButton->new( 351 | $self->panel, 352 | -1, 353 | q{« }, 354 | [-1, -1], 355 | [-1, -1], 356 | ); 357 | } 358 | 359 | sub _build_btn_project { 360 | my $self = shift; 361 | 362 | return Wx::Button->new( 363 | $self->panel, 364 | -1, 365 | q{Project}, 366 | [ -1, -1 ], 367 | [ -1, -1 ], 368 | wxRB_GROUP, # first button in group 369 | ); 370 | } 371 | 372 | sub _build_btn_plan_sel { 373 | my $self = shift; 374 | 375 | return Wx::RadioButton->new( 376 | $self->panel, 377 | -1, 378 | q{« }, 379 | [-1, -1], 380 | [-1, -1], 381 | ); 382 | } 383 | 384 | sub _build_btn_plan { 385 | my $self = shift; 386 | 387 | return Wx::Button->new( 388 | $self->panel, 389 | -1, 390 | q{Plan}, 391 | [ -1, -1 ], 392 | [ -1, -1 ], 393 | ); 394 | } 395 | 396 | sub _set_events { } 397 | 398 | 1; 399 | 400 | =head1 AUTHOR 401 | 402 | Stefan Suciu, C<< >> 403 | 404 | =head1 BUGS 405 | 406 | None known. 407 | 408 | Please report any bugs or feature requests to the author. 409 | 410 | =head1 ACKNOWLEDGMENTS 411 | 412 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 413 | project: 414 | 415 | https://github.com/tmtowtdi/LacunaWaX 416 | 417 | Copyright: Jonathan D. Barton 2012-2013 418 | 419 | Thank you! 420 | 421 | =head1 LICENSE AND COPYRIGHT 422 | 423 | Stefan Suciu 2013 424 | 425 | This program is free software; you can redistribute it and/or modify it 426 | under the terms of either: the GNU General Public License as published 427 | by the Free Software Foundation. 428 | 429 | =cut 430 | 431 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/View/Panel/Top.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::View::Panel::Top; 2 | 3 | # ABSTRACT: The Top Panel 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | WxPanel 10 | WxSizer 11 | ); 12 | use Wx qw(:allclasses :everything); 13 | use Wx::Event qw(EVT_CLOSE); 14 | 15 | with 'App::Sqitch::GUI::Roles::Element'; 16 | 17 | has 'panel' => ( 18 | is => 'ro', 19 | isa => WxPanel, 20 | lazy => 1, 21 | builder => '_build_panel', 22 | ); 23 | 24 | has 'sizer' => ( 25 | is => 'ro', 26 | isa => WxSizer, 27 | lazy => 1, 28 | builder => '_build_sizer', 29 | ); 30 | 31 | sub BUILD { 32 | my $self = shift; 33 | 34 | $self->panel->Show(0); 35 | $self->panel->SetSizer( $self->sizer ); 36 | $self->panel->Show(1); 37 | 38 | return $self; 39 | } 40 | 41 | sub _build_panel { 42 | my $self = shift; 43 | 44 | my $panel = Wx::Panel->new( 45 | $self->parent, 46 | -1, 47 | [ -1, -1 ], 48 | [ -1, -1 ], 49 | wxFULL_REPAINT_ON_RESIZE, 50 | 'mainPanel', 51 | ); 52 | #$panel->SetBackgroundColour(Wx::Colour->new('yellow')); 53 | 54 | return $panel; 55 | } 56 | 57 | sub _build_sizer { 58 | return Wx::BoxSizer->new(wxHORIZONTAL); 59 | } 60 | 61 | sub _set_events { } 62 | 63 | sub OnClose { 64 | my ($self, $event) = @_; 65 | } 66 | 67 | =head1 AUTHOR 68 | 69 | Stefan Suciu, C<< >> 70 | 71 | =head1 BUGS 72 | 73 | None known. 74 | 75 | Please report any bugs or feature requests to the author. 76 | 77 | =head1 ACKNOWLEDGMENTS 78 | 79 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 80 | project: 81 | 82 | https://github.com/tmtowtdi/LacunaWaX 83 | 84 | Copyright: Jonathan D. Barton 2012-2013 85 | 86 | Thank you! 87 | 88 | =head1 LICENSE AND COPYRIGHT 89 | 90 | Stefan Suciu 2013 91 | 92 | This program is free software; you can redistribute it and/or modify it 93 | under the terms of either: the GNU General Public License as published 94 | by the Free Software Foundation. 95 | 96 | =cut 97 | 98 | 1; # End of App::Sqitch::GUI::Panel::Top 99 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Wx/Editor.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Wx::Editor; 2 | 3 | # ABSTRACT: Wx StyledText Editor Control 4 | 5 | use Moo; 6 | use Wx::Scintilla 0.34 (); 7 | use Wx qw( 8 | wxDEFAULT 9 | wxNORMAL 10 | wxSTC_LEX_MSSQL 11 | wxSTC_MARGIN_SYMBOL 12 | wxSTC_STYLE_BRACEBAD 13 | wxSTC_STYLE_BRACELIGHT 14 | wxSTC_STYLE_DEFAULT 15 | wxSTC_WRAP_NONE 16 | ); 17 | use Wx::Event; 18 | 19 | with 'App::Sqitch::GUI::Roles::Element'; 20 | 21 | extends 'Wx::Scintilla::TextCtrl'; 22 | 23 | sub FOREIGNBUILDARGS { 24 | my $self = shift; 25 | my %args = @_; 26 | return ( 27 | $args{parent}, 28 | -1, 29 | [-1, -1], 30 | [-1, -1], 31 | ); 32 | } 33 | 34 | sub BUILD { 35 | my $self = shift; 36 | 37 | # 38 | # From QDepo (http://sourceforge.net/projects/tpda-qrt/) ;) 39 | # 40 | $self->SetMarginType( 1, wxSTC_MARGIN_SYMBOL ); 41 | $self->SetMarginWidth( 1, 10 ); 42 | $self->StyleSetFont( wxSTC_STYLE_DEFAULT, 43 | Wx::Font->new( 10, wxDEFAULT, wxNORMAL, wxNORMAL, 0, 'Courier New' ) ); 44 | # $self->SetLexer( wxSTC_LEX_SQL ); 45 | $self->SetLexer( wxSTC_LEX_MSSQL ); 46 | # List0 47 | $self->SetKeyWords(0, 48 | q{all and any ascending between by cast collate containing day 49 | descending distinct escape exists from full group having in 50 | index inner into is join left like merge month natural not 51 | null on order outer plan right select singular some sort starting 52 | transaction union upper user where with year} ); 53 | # List1 54 | $self->SetKeyWords(1, 55 | q{blob char decimal integer number varchar} ); 56 | # List2 Only for MSSQL? 57 | $self->SetKeyWords(2, q{avg count gen_id max min sum} ); 58 | $self->SetTabWidth(4); 59 | $self->SetIndent(4); 60 | $self->SetHighlightGuide(4); 61 | 62 | $self->StyleClearAll(); 63 | 64 | # Global default styles for all languages 65 | $self->StyleSetSpec( wxSTC_STYLE_BRACELIGHT, 66 | "fore:#FFFFFF,back:#0000FF,bold" ); 67 | $self->StyleSetSpec( wxSTC_STYLE_BRACEBAD, 68 | "fore:#000000,back:#FF0000,bold" ); 69 | 70 | # MSSQL - works with wxSTC_LEX_MSSQL 71 | $self->StyleSetSpec(0, "fore:#000000"); #*Default 72 | $self->StyleSetSpec(1, "fore:#ff7373,italic"); #*Comment 73 | $self->StyleSetSpec(2, "fore:#007f7f,italic"); #*Commentline 74 | $self->StyleSetSpec(3, "fore:#0000ff"); #*Number 75 | $self->StyleSetSpec(4, "fore:#dca3a3"); #*Singlequoted 76 | $self->StyleSetSpec(5, "fore:#3f3f3f"); #*Operation 77 | $self->StyleSetSpec(6, "fore:#000000"); #*Identifier 78 | $self->StyleSetSpec(7, "fore:#8cd1d3"); #*@-Variable 79 | $self->StyleSetSpec(8, "fore:#705050"); #*Doublequoted 80 | $self->StyleSetSpec(9, "fore:#dfaf8f"); #*List0 81 | $self->StyleSetSpec(10,"fore:#94c0f3"); #*List1 82 | $self->StyleSetSpec(11,"fore:#705030"); #*List2 83 | 84 | return $self; 85 | }; 86 | 87 | sub _set_events { 88 | return 1; 89 | } 90 | 91 | 1; 92 | 93 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Wx/Listctrl.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Wx::Listctrl; 2 | 3 | # ABSTRACT: Virtual List View Control 4 | 5 | use 5.010; 6 | use Moo; 7 | use Types::Standard qw( 8 | ArrayRef 9 | HashRef 10 | ); 11 | use Wx qw( 12 | wxLC_REPORT 13 | wxLC_SINGLE_SEL 14 | wxLC_VIRTUAL 15 | wxLIST_FORMAT_CENTER 16 | wxLIST_FORMAT_LEFT 17 | wxLIST_FORMAT_RIGHT 18 | ); 19 | use Wx qw(:listctrl); 20 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 21 | use App::Sqitch::X qw(hurl); 22 | 23 | extends 'Wx::ListView'; 24 | 25 | has 'list_data' => ( 26 | is => 'rw', 27 | ); 28 | 29 | has 'meta_data' => ( 30 | is => 'rw', 31 | isa => ArrayRef, 32 | ); 33 | 34 | sub FOREIGNBUILDARGS { 35 | my $self = shift; 36 | my %args = @_; 37 | return ( 38 | $args{parent}, 39 | -1, 40 | [ -1, -1 ], 41 | [ -1, -1 ], 42 | wxLC_REPORT | wxLC_VIRTUAL | wxLC_SINGLE_SEL 43 | ); 44 | } 45 | 46 | sub BUILD { 47 | my $self = shift; 48 | $self->add_columns( $self->meta_data ); 49 | return $self; 50 | } 51 | 52 | sub add_columns { 53 | my ( $self, $header ) = @_; 54 | my $cnt = 0; 55 | foreach my $rec ( @{$header} ) { 56 | my $label = $rec->{label}; 57 | my $width = $rec->{width}; 58 | my $align 59 | = $rec->{align} eq 'left' ? wxLIST_FORMAT_LEFT 60 | : $rec->{align} eq 'center' ? wxLIST_FORMAT_CENTER 61 | : $rec->{align} eq 'right' ? wxLIST_FORMAT_RIGHT 62 | : wxLIST_FORMAT_LEFT; 63 | $self->InsertColumn( $cnt, $label, $align, $width ); 64 | $cnt++; 65 | } 66 | return; 67 | } 68 | 69 | sub OnGetItemText { 70 | my( $self, $item, $column ) = @_; 71 | return $self->list_data->get_value( $item, $column ); 72 | } 73 | 74 | sub OnGetItemAttr { 75 | my( $self, $item ) = @_; 76 | my $attr = Wx::ListItemAttr->new; 77 | $attr->SetBackgroundColour( Wx::Colour->new('LIGHT YELLOW') ) 78 | if $item % 2 == 0; 79 | return $attr; 80 | } 81 | 82 | sub RefreshList { 83 | my $self = shift; 84 | my $item_count = $self->list_data->get_item_count; 85 | $self->SetItemCount( $item_count ); 86 | $self->RefreshItems(0, $item_count); 87 | return; 88 | } 89 | 90 | sub set_selection { 91 | my ( $self, $item ) = @_; 92 | hurl 'Wrong argument passed to set_selection()' unless defined $item; 93 | my $index 94 | = $item eq q{} ? 0 95 | : $item eq 'first' ? 0 96 | : $item eq 'last' ? ( $self->get_item_count - 1 ) 97 | : $item; 98 | $self->Select( $index, 1 ); # 1|0 <=> select|deselect 99 | $self->EnsureVisible($index); 100 | return $index; 101 | } 102 | 103 | sub get_selection { 104 | my $self = shift; 105 | return $self->GetFirstSelected; 106 | } 107 | 108 | sub get_item_count { 109 | my $self = shift; 110 | return $self->GetItemCount; 111 | } 112 | 113 | sub delete_current_item { 114 | my $self = shift; 115 | return unless $self->get_item_count > 0; 116 | my $sel = $self->get_selection; 117 | return unless defined $sel and $sel >= 0; 118 | $self->list_data->remove_row($sel); 119 | $self->Select( 0, 1 ); # 1|0 <=> select|deselect 120 | $self->RefreshList; 121 | return; 122 | } 123 | 124 | sub delete_all_items { 125 | my $self = shift; 126 | my $count = $self->get_item_count; 127 | $self->list_data->remove_row(0, $count); 128 | $self->RefreshItems( 0, 0 ); 129 | $self->Select( 0, 0 ); # 1|0 <=> select|deselect 130 | return; 131 | } 132 | 133 | sub _set_events { } 134 | 135 | 1; 136 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Wx/LogView.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Wx::LogView; 2 | 3 | # ABSTRACT: Logger control with syntax highlighting 4 | 5 | use Moo; 6 | use Wx::Scintilla 0.34 (); 7 | use Wx qw( 8 | wxDEFAULT 9 | wxNORMAL 10 | wxSTC_LEX_MSSQL 11 | wxSTC_MARGIN_SYMBOL 12 | wxSTC_STYLE_DEFAULT 13 | wxSTC_WRAP_NONE 14 | ); 15 | use Wx::Event; 16 | 17 | with 'App::Sqitch::GUI::Roles::Element'; 18 | 19 | extends 'Wx::Scintilla::TextCtrl'; 20 | 21 | sub FOREIGNBUILDARGS { 22 | my $self = shift; 23 | my %args = @_; 24 | return ( 25 | $args{parent}, 26 | -1, 27 | [-1, -1], 28 | [-1, -1], 29 | ); 30 | } 31 | 32 | sub BUILD { 33 | my $self = shift; 34 | 35 | $self->StyleSetBackground( wxSTC_STYLE_DEFAULT, Wx::Colour->new('WHEAT') ); 36 | $self->SetMarginType( 1, wxSTC_MARGIN_SYMBOL ); 37 | $self->SetMarginWidth( 1, 0 ); 38 | $self->SetWrapMode(wxSTC_WRAP_NONE); # wxSTC_WRAP_WORD 39 | $self->StyleSetFont( wxSTC_STYLE_DEFAULT, 40 | Wx::Font->new( 10, wxDEFAULT, wxNORMAL, wxNORMAL, 0, 'Courier New' ) 41 | ); 42 | $self->SetLexer(wxSTC_LEX_MSSQL); 43 | $self->SetWrapMode(wxSTC_WRAP_NONE); # wxSTC_WRAP_WORD 44 | 45 | # List0 46 | $self->SetKeyWords( 0, q{ii} ); 47 | 48 | # List1 49 | $self->SetKeyWords( 1, q{ee} ); 50 | 51 | # List2 52 | $self->SetKeyWords( 2, q{ww} ); 53 | 54 | $self->SetTabWidth(4); 55 | $self->SetIndent(4); 56 | $self->SetHighlightGuide(4); 57 | $self->StyleClearAll(); 58 | 59 | # MSSQL - works with wxSTC_LEX_MSSQL 60 | $self->StyleSetSpec( 3, "fore:#0000ff" ); #*Number 61 | $self->StyleSetSpec( 4, "fore:#dca3a3" ); #*Singlequoted 62 | $self->StyleSetSpec( 8, "fore:#705050" ); #*Doublequoted 63 | $self->StyleSetSpec( 9, "fore:#00ff00" ); #*List0 64 | $self->StyleSetSpec( 10, "fore:#ff0000" ); #*List1 65 | $self->StyleSetSpec( 11, "fore:#0000ff" ); #*List2 66 | 67 | return $self; 68 | } 69 | 70 | sub _set_events { 71 | return 1; 72 | } 73 | 74 | 1; 75 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Wx/Menubar.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Wx::Menubar; 2 | 3 | # ABSTRACT: Wx Menubar Control 4 | 5 | use Moo; 6 | use App::Sqitch::GUI::Types qw( 7 | ArrayRef 8 | WxMenuBar 9 | ); 10 | use Wx qw(:everything); 11 | use Wx::Event qw(EVT_MENU); 12 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 13 | 14 | with 'App::Sqitch::GUI::Roles::Element'; 15 | 16 | use App::Sqitch::GUI::View::Dialog::Help; 17 | use App::Sqitch::GUI::View::Dialog::About; 18 | 19 | sub BUILD {}; # required by the role (?!) 20 | 21 | has 'menu_bar' => ( 22 | is => 'ro', 23 | isa => WxMenuBar, 24 | lazy => 1, 25 | builder => '_build_menu_bar', 26 | ); 27 | 28 | sub _build_menu_bar { 29 | my $self = shift; 30 | 31 | # App menu 32 | my $app_menu = Wx::Menu->new; 33 | $app_menu->Append( $self->item_quit ); 34 | 35 | # Admin menu 36 | my $admin_menu = Wx::Menu->new; 37 | $admin_menu->Append( $self->item_admin ); 38 | 39 | # Help menu 40 | my $help_menu = Wx::Menu->new; 41 | $help_menu->Append( $self->item_help ); 42 | $help_menu->Append( $self->item_about ); 43 | 44 | my $menu_bar = Wx::MenuBar->new; 45 | $menu_bar->Append( $app_menu, __ "&App" ); 46 | $menu_bar->Append( $admin_menu, __ "A&dmin" ); 47 | $menu_bar->Append( $help_menu, __ "&Help" ); 48 | 49 | return $menu_bar; 50 | } 51 | 52 | sub item_quit { 53 | my $self = shift; 54 | return Wx::MenuItem->new( 55 | undef, 56 | wxID_EXIT, 57 | "&Quit\tCtrl+Q", 58 | 'Quit', 59 | wxITEM_NORMAL, 60 | undef # if defined, this is a sub-menu 61 | ); 62 | } 63 | 64 | sub item_admin { 65 | my $self = shift; 66 | return Wx::MenuItem->new( 67 | undef, 68 | 2001, 69 | "Projects\tCtrl+P", 70 | 'Projects', 71 | wxITEM_NORMAL, 72 | undef # if defined, this is a sub-menu 73 | ); 74 | } 75 | 76 | sub item_help { 77 | my $self = shift; 78 | return Wx::MenuItem->new( 79 | undef, 80 | wxID_HELP, 81 | '&Help', 82 | 'Show HTML help', 83 | wxITEM_NORMAL, 84 | undef # if defined, this is a sub-menu 85 | ); 86 | } 87 | 88 | sub item_about { 89 | my $self = shift; 90 | return Wx::MenuItem->new( 91 | undef, 92 | wxID_ABOUT, 93 | '&About', 94 | 'Show about dialog', 95 | wxITEM_NORMAL, 96 | undef # if defined, this is a sub-menu 97 | ); 98 | } 99 | 100 | # # If you add a new menu to the bar, be sure to add its name to this list 101 | # has 'menu_list' => ( 102 | # is => 'rw', 103 | # isa => ArrayRef, 104 | # lazy => 1, 105 | # default => sub { [ qw( 106 | # menu_app 107 | # menu_admin 108 | # menu_help 109 | # ) ]; 110 | # }, 111 | # ); 112 | 113 | sub _set_events { 114 | my $self = shift; 115 | EVT_MENU $self->parent, $self->item_about->GetId, sub{$self->OnAbout(@_)}; 116 | EVT_MENU $self->parent, $self->item_help->GetId, sub{$self->OnHelp(@_)}; 117 | return 1; 118 | } 119 | 120 | sub OnAbout { 121 | my $self = shift; 122 | my $frame = shift; # Wx::Frame 123 | my $event = shift; # Wx::CommandEvent 124 | my $d = App::Sqitch::GUI::View::Dialog::About->new( 125 | app => $self->app, 126 | ancestor => $self, 127 | parent => undef, 128 | ); 129 | $d->show(); 130 | return 1; 131 | } 132 | 133 | sub OnHelp { 134 | my $self = shift; 135 | my $frame = shift; # Wx::Frame 136 | my $event = shift; # Wx::CommandEvent 137 | my $d = App::Sqitch::GUI::View::Dialog::Help->new( 138 | app => $self->app, 139 | ancestor => $self, 140 | parent => undef, 141 | ); 142 | return 1; 143 | } 144 | 145 | 1; 146 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Wx/Notebook.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Wx::Notebook; 2 | 3 | # ABSTRACT: Wx Notebook Control 4 | 5 | use 5.010; 6 | use utf8; 7 | use Moo; 8 | use App::Sqitch::GUI::Types qw( 9 | WxPanel 10 | WxSizer 11 | WxCollapsiblePane 12 | WxStaticText 13 | WxTextCtrl 14 | SqitchGUIWxNotebook 15 | SqitchGUIWxEditor 16 | ); 17 | use Wx qw(:everything); 18 | use Wx::Event qw(); 19 | use Wx::AUI; 20 | 21 | with 'App::Sqitch::GUI::Roles::Element'; 22 | 23 | extends 'Wx::AuiNotebook'; 24 | 25 | has 'page_deploy' => ( 26 | is => 'rw', 27 | isa => WxPanel, 28 | lazy => 1, 29 | builder => '_build_page_deploy', 30 | ); 31 | 32 | has 'page_revert' => ( 33 | is => 'rw', 34 | isa => WxPanel, 35 | lazy => 1, 36 | builder => '_build_page_revert', 37 | ); 38 | 39 | has 'page_verify' => ( 40 | is => 'rw', 41 | isa => WxPanel, 42 | lazy => 1, 43 | builder => '_build_page_verify', 44 | ); 45 | 46 | 47 | sub FOREIGNBUILDARGS { 48 | my $self = shift; 49 | my %args = @_; 50 | return ( 51 | $args{parent}, 52 | -1, 53 | [-1, -1], 54 | [-1, -1], 55 | wxAUI_NB_TAB_FIXED_WIDTH, 56 | ); 57 | } 58 | 59 | sub BUILD { 60 | my $self = shift; 61 | return $self; 62 | }; 63 | 64 | sub _build_page_deploy { 65 | my $self = shift; 66 | my $page = Wx::Panel->new( $self->parent, -1, [-1, -1], [-1, -1] ); 67 | $self->AddPage( $page, 'Deploy' ); 68 | return $page; 69 | } 70 | 71 | sub _build_page_revert { 72 | my $self = shift; 73 | my $page = Wx::Panel->new( $self->parent, -1, [-1, -1], [-1, -1] ); 74 | $self->AddPage( $page, 'Revert' ); 75 | return $page; 76 | } 77 | 78 | sub _build_page_verify { 79 | my $self = shift; 80 | my $page = Wx::Panel->new( $self->parent, -1, [-1, -1], [-1, -1] ); 81 | $self->AddPage( $page, 'Verify' ); 82 | return $page; 83 | } 84 | 85 | sub _set_events { } 86 | 87 | 1; 88 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Wx/Statusbar.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Wx::Statusbar; 2 | 3 | # ABSTRACT: Wx Statusbar Control 4 | 5 | use Moo; 6 | use App::Sqitch::GUI::Types qw( 7 | Str 8 | WxStatusBar 9 | ); 10 | use Wx qw(:everything); 11 | use Wx::Event qw(EVT_SIZE); 12 | 13 | with 'App::Sqitch::GUI::Roles::Element'; 14 | 15 | has 'status_bar' => ( 16 | is => 'rw', 17 | isa => WxStatusBar, 18 | lazy => 1, 19 | builder => '_build_status_bar', 20 | ); 21 | 22 | has 'caption_msg' => ( 23 | is => 'rw', 24 | isa => Str, 25 | lazy => 1, 26 | builder => '_build_caption_msg', 27 | ); 28 | 29 | has 'caption_state' => ( 30 | is => 'rw', 31 | isa => Str, 32 | lazy => 1, 33 | builder => '_build_caption_state', 34 | ); 35 | 36 | has 'caption_proj' => ( 37 | is => 'rw', 38 | isa => Str, 39 | lazy => 1, 40 | builder => '_build_caption_proj', 41 | ); 42 | 43 | sub BUILD { 44 | my $self = shift; 45 | my $sb = $self->bar_reset; 46 | return $sb; 47 | } 48 | 49 | sub _build_status_bar { 50 | my $self = shift; 51 | return $self->parent->CreateStatusBar(3); 52 | } 53 | 54 | sub _build_caption_msg { 55 | my $self = shift; 56 | return q{}; 57 | } 58 | 59 | sub _build_caption_state { 60 | my $self = shift; 61 | return q{}; 62 | } 63 | 64 | sub _build_caption_proj { 65 | my $self = shift; 66 | return q{}; 67 | } 68 | 69 | sub _set_events { 70 | my $self = shift; 71 | EVT_SIZE( $self->status_bar, sub { $self->OnResize(@_) } ); 72 | return 1; 73 | } 74 | 75 | sub bar_reset { 76 | my $self = shift; 77 | 78 | $self->status_bar->DestroyChildren(); 79 | $self->status_bar->SetStatusWidths(-1, 100, 100); 80 | $self->status_bar->SetStatusText($self->caption_msg, 0); 81 | $self->status_bar->SetStatusText($self->caption_state, 1); 82 | $self->status_bar->SetStatusText($self->caption_proj, 2); 83 | $self->status_bar->Update; 84 | 85 | return $self->status_bar; 86 | } 87 | 88 | sub change_caption { 89 | my ($self, $new_text, $field_no) = @_; 90 | 91 | my $old_text = $self->status_bar->GetStatusText($field_no); 92 | $self->caption_msg($new_text) if $field_no == 0; 93 | $self->caption_state($new_text) if $field_no == 1; 94 | $self->caption_proj($new_text) if $field_no == 2; 95 | $self->status_bar->SetStatusText( $new_text, $field_no ); 96 | 97 | return $old_text; 98 | } 99 | 100 | sub OnResize { 101 | my($self, $status_bar, $event) = @_; 102 | return 1; 103 | } 104 | 105 | =head1 AUTHOR 106 | 107 | Stefan Suciu, C<< >> 108 | 109 | =head1 BUGS 110 | 111 | None known. 112 | 113 | Please report any bugs or feature requests to the author. 114 | 115 | =head1 ACKNOWLEDGMENTS 116 | 117 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 118 | project: 119 | 120 | https://github.com/tmtowtdi/LacunaWaX 121 | 122 | Copyright: Jonathan D. Barton 2012-2013 123 | 124 | Thank you! 125 | 126 | =head1 LICENSE AND COPYRIGHT 127 | 128 | Stefan Suciu 2013 129 | 130 | This program is free software; you can redistribute it and/or modify it 131 | under the terms of either: the GNU General Public License as published 132 | by the Free Software Foundation. 133 | 134 | =cut 135 | 136 | 1; # End of App::Sqitch::GUI::View::StatusBar 137 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/Wx/Toolbar.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::Wx::Toolbar; 2 | 3 | # ABSTRACT: Wx Toolbar Control 4 | 5 | use Moo; 6 | use App::Sqitch::GUI::Types qw( 7 | Dir 8 | ); 9 | use Wx qw( 10 | wxBITMAP_TYPE_ANY 11 | wxITEM_NORMAL 12 | wxNO_BORDER 13 | wxNullBitmap 14 | wxTB_DOCKABLE 15 | wxTB_FLAT 16 | wxTB_HORIZONTAL 17 | ); 18 | use Wx::ArtProvider qw(:artid); 19 | use Path::Class qw(file); 20 | use Locale::TextDomain 1.20 qw(App-Sqitch-GUI); 21 | use App::Sqitch::X qw(hurl); 22 | 23 | extends 'Wx::ToolBar'; 24 | 25 | has 'icon_path' => ( 26 | is => 'ro', 27 | isa => Dir, 28 | ); 29 | 30 | sub FOREIGNBUILDARGS { 31 | my $self = shift; 32 | my %args = @_; 33 | return ( 34 | $args{parent}, 35 | -1, 36 | [ -1, -1 ], 37 | [ -1, -1 ], 38 | wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT | wxTB_DOCKABLE, 5050, 39 | ); 40 | } 41 | 42 | sub BUILD { 43 | my $self = shift; 44 | 45 | $self->SetToolBitmapSize( Wx::Size->new( 22, 22 ) ); 46 | $self->SetMargins( 4, 4 ); 47 | 48 | return $self; 49 | } 50 | 51 | sub make_toolbar_button { 52 | my ( $self, $name, $attribs ) = @_; 53 | my $type = $attribs->{type}; 54 | if ( $self->can($type) ) { 55 | $self->$type( $name, $attribs ); 56 | } 57 | else { 58 | hurl "Unknown toolbar_button type attribute: $type"; 59 | } 60 | return; 61 | } 62 | 63 | sub set_initial_mode { 64 | my ( $self, $names ) = @_; 65 | foreach my $name ( @{$names} ) { 66 | 67 | # Initial state disabled, except quit and project buttons 68 | next if $name eq 'tb_qt'; 69 | next if $name eq 'tb_pj'; 70 | $self->enable_tool( $name, 0 ); # 0 = disabled 71 | } 72 | return; 73 | } 74 | 75 | sub _item_normal { ## no critic (ProhibitUnusedPrivateSubroutines) 76 | my ( $self, $name, $attribs ) = @_; 77 | 78 | $self->AddSeparator if $attribs->{sep} =~ m{before}x; 79 | 80 | # Add the button 81 | $self->{$name} = $self->AddTool( 82 | $attribs->{id}, $self->make_bitmap( $attribs->{icon} ), 83 | wxNullBitmap, wxITEM_NORMAL, 84 | undef, $attribs->{tooltip}, 85 | $attribs->{help}, 86 | ); 87 | 88 | $self->AddSeparator if $attribs->{sep} =~ m{after}x; 89 | 90 | return; 91 | } 92 | 93 | sub get_toolbar_btn { 94 | my ( $self, $name ) = @_; 95 | return $self->{$name}; 96 | } 97 | 98 | sub enable_tool { 99 | my ( $self, $btn_name, $state ) = @_; 100 | my $tb_btn_id = $self->get_toolbar_btn($btn_name)->GetId; 101 | my $new_state; 102 | if ( defined $state ) { 103 | $new_state 104 | = $state eq q{} ? 0 105 | : $state =~ m/normal/x ? 1 106 | : $state =~ m/disabled/x ? 0 107 | : $state ? 1 108 | : 0; # last for: 1|0 109 | } 110 | else { 111 | $new_state = !$self->GetToolState($tb_btn_id); # undef state: toggle 112 | } 113 | $self->EnableTool( $tb_btn_id, $new_state ); 114 | return; 115 | } 116 | 117 | sub toggle_tool_check { 118 | my ( $self, $btn_name, $state ) = @_; 119 | my $tb_btn_id = $self->get_toolbar_btn($btn_name)->GetId; 120 | $self->ToggleTool( $tb_btn_id, $state ); 121 | return; 122 | } 123 | 124 | sub make_bitmap { 125 | my ( $self, $icon_file_name ) = @_; 126 | my $icon = file $self->icon_path, $icon_file_name; 127 | return Wx::Bitmap->new( $icon->stringify, wxBITMAP_TYPE_ANY ) if -f $icon; 128 | return Wx::ArtProvider::GetBitmap( wxART_ERROR ); 129 | } 130 | 131 | 1; 132 | 133 | =head1 SYNOPSIS 134 | 135 | =head2 new 136 | 137 | Constructor method. 138 | 139 | =head2 make_toolbar_button 140 | 141 | Make toolbar button. 142 | 143 | =head2 set_initial_mode 144 | 145 | Disable some of the toolbar buttons. 146 | 147 | =head2 _item_normal 148 | 149 | Create a normal toolbar button 150 | 151 | =head2 _item_check 152 | 153 | Create a check toolbar button 154 | 155 | =head2 _item_list 156 | 157 | Create a list toolbar button. Not used. 158 | 159 | =head2 get_toolbar_btn 160 | 161 | Return a toolbar button by name. 162 | 163 | =head2 get_choice_options 164 | 165 | Return all options or the name of the option with index 166 | 167 | =head2 enable_tool 168 | 169 | Toggle tool bar button. If state is defined then set to state, do not toggle. 170 | 171 | State can come as 0 | 1 and normal | disabled. Because toolbar.yml is used for 172 | both Tk and Wx, this sub is more complex that is should be. 173 | 174 | =head2 toggle_tool_check 175 | 176 | Toggle a toolbar checkbutton. State can come as 0 | 1. 177 | 178 | =head2 make_bitmap 179 | 180 | Create and return a bitmap object, of any type. 181 | 182 | =cut 183 | -------------------------------------------------------------------------------- /lib/App/Sqitch/GUI/WxApp.pm: -------------------------------------------------------------------------------- 1 | package App::Sqitch::GUI::WxApp; 2 | 3 | # ABSTRACT: Wx App Extension 4 | 5 | use 5.010; 6 | use Moo; 7 | use App::Sqitch::GUI::Types qw( 8 | Maybe 9 | SqitchGUIConfig 10 | SqitchGUIModel 11 | SqitchGUIView 12 | ); 13 | use Wx; 14 | use Wx::Event qw(EVT_CLOSE); 15 | 16 | extends 'Wx::App'; 17 | 18 | use App::Sqitch::GUI::Model; 19 | use App::Sqitch::GUI::View; 20 | 21 | has config => ( 22 | is => 'ro', 23 | isa => Maybe[SqitchGUIConfig], 24 | lazy => 1, 25 | required => 1, 26 | ); 27 | 28 | has 'view' => ( 29 | is => 'ro', 30 | isa => SqitchGUIView, 31 | lazy => 1, 32 | builder => '_build_view', 33 | handles => { 34 | menu_bar => 'menu_bar', 35 | }, 36 | ); 37 | 38 | has 'model' => ( 39 | is => 'ro', 40 | isa => SqitchGUIModel, 41 | lazy => 1, 42 | required => 1, 43 | ); 44 | 45 | sub FOREIGNBUILDARGS { 46 | return (); # Wx::App->new() gets no arguments. 47 | } 48 | 49 | sub BUILD { 50 | my $self = shift; 51 | 52 | $self->SetTopWindow( $self->view->frame ); 53 | $self->view->frame->Show(1); 54 | $self->_set_events; 55 | 56 | return $self; 57 | } 58 | 59 | sub _build_view { 60 | my $self = shift; 61 | my $args = { 62 | app => $self, 63 | config => $self->config, 64 | title => 'Sqitch::GUI', 65 | }; 66 | my $view = App::Sqitch::GUI::View->new( $args ); 67 | return $view; 68 | } 69 | 70 | sub _set_events { 71 | my $self = shift; 72 | EVT_CLOSE( $self->view->frame, sub { $self->OnClose(@_) } ); 73 | return; 74 | } 75 | 76 | sub OnInit { 77 | my $self = shift; 78 | Wx::InitAllImageHandlers(); 79 | return 1; 80 | } 81 | 82 | sub OnClose { 83 | my($self, $frame, $event) = @_; 84 | $event->Skip(); 85 | return; 86 | } 87 | 88 | sub OnAssertFailure { 89 | my ( $self, $file, $line, $function, $condition, $msg ) = @_; 90 | print "AssertFailure: $file, $line, $function, $condition, $msg\n"; 91 | } 92 | 93 | =head1 AUTHOR 94 | 95 | Stefan Suciu, C<< >> 96 | 97 | =head1 BUGS 98 | 99 | None known. 100 | 101 | Please report any bugs or feature requests to the author. 102 | 103 | =head1 ACKNOWLEDGMENTS 104 | 105 | GUI with Wx and Moose heavily inspired/copied from the LacunaWaX 106 | project: 107 | 108 | https://github.com/tmtowtdi/LacunaWaX 109 | 110 | Copyright: Jonathan D. Barton 2012-2013 111 | 112 | Thank you! 113 | 114 | =head1 LICENSE AND COPYRIGHT 115 | 116 | Stefan Suciu 2013 117 | 118 | This program is free software; you can redistribute it and/or modify it 119 | under the terms of either: the GNU General Public License as published 120 | by the Free Software Foundation. 121 | 122 | =cut 123 | 124 | 1; 125 | -------------------------------------------------------------------------------- /po/App-Sqitch-GUI.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR Ștefan Suciu 3 | # This file is distributed under the same license as the App-Sqitch-GUI package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: App-Sqitch-GUI 0.020\n" 10 | "Report-Msgid-Bugs-To: stefbv70@gmail.com\n" 11 | "POT-Creation-Date: 2018-06-10 09:32+0300\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" 20 | 21 | #: lib/App/Sqitch/GUI/Config.pm:138 22 | #, perl-brace-format 23 | msgid "The configuration file \"{file}\" does not exist!" 24 | msgstr "" 25 | 26 | #: lib/App/Sqitch/GUI/Config.pm:143 27 | #, perl-brace-format 28 | msgid "The configuration file \"{file}\" could not be (re)loaded: {err}" 29 | msgstr "" 30 | 31 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:24 32 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:27 33 | msgid "Projects" 34 | msgstr "" 35 | 36 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:36 37 | msgid "Quit" 38 | msgstr "" 39 | 40 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:39 41 | msgid "Quit the application" 42 | msgstr "" 43 | 44 | #: lib/App/Sqitch/GUI/Controller.pm:70 45 | #, perl-brace-format 46 | msgid "EE Configuration error: \"{error}\"" 47 | msgstr "" 48 | 49 | #: lib/App/Sqitch/GUI/Controller.pm:91 50 | #, perl-brace-format 51 | msgid "EE Model error: \"{error}\"" 52 | msgstr "" 53 | 54 | #: lib/App/Sqitch/GUI/Controller.pm:268 55 | #, perl-brace-format 56 | msgid "II OK, found a project" 57 | msgid_plural "II Found {count} projects" 58 | msgstr[0] "" 59 | msgstr[1] "" 60 | 61 | #: lib/App/Sqitch/GUI/Controller.pm:293 62 | msgid "II No valid default project, select the project and load it" 63 | msgid_plural "II No valid default project, select a project and load it" 64 | msgstr[0] "" 65 | msgstr[1] "" 66 | 67 | #: lib/App/Sqitch/GUI/Controller.pm:306 lib/App/Sqitch/GUI/Controller.pm:475 68 | #: lib/App/Sqitch/GUI/Controller.pm:659 lib/App/Sqitch/GUI/Controller.pm:842 69 | msgid "Yes" 70 | msgstr "" 71 | 72 | #: lib/App/Sqitch/GUI/Controller.pm:323 73 | msgid "EE Something went wrong, no project \"name\" and path\"" 74 | msgstr "" 75 | 76 | #: lib/App/Sqitch/GUI/Controller.pm:338 77 | msgid "II Load plan item feature not implmented yet!" 78 | msgstr "" 79 | 80 | #: lib/App/Sqitch/GUI/Controller.pm:363 lib/App/Sqitch/GUI/Model.pm:130 81 | #, perl-brace-format 82 | msgid "EE The \"{path}\" path does not look like a Sqitch project!" 83 | msgstr "" 84 | 85 | #: lib/App/Sqitch/GUI/Controller.pm:371 86 | #, perl-brace-format 87 | msgid "II Can not cd to {path}\"" 88 | msgstr "" 89 | 90 | #: lib/App/Sqitch/GUI/Controller.pm:395 91 | #, perl-brace-format 92 | msgid "II Loading the \"{name}\" project" 93 | msgstr "" 94 | 95 | #: lib/App/Sqitch/GUI/Controller.pm:568 96 | msgid "II No changes defined yet" 97 | msgstr "" 98 | 99 | #: lib/App/Sqitch/GUI/Controller.pm:571 100 | msgid "II Loading changes..." 101 | msgstr "" 102 | 103 | #: lib/App/Sqitch/GUI/Controller.pm:792 104 | msgid "WW Select a project item, please" 105 | msgstr "" 106 | 107 | #: lib/App/Sqitch/GUI/Model.pm:94 108 | #, perl-brace-format 109 | msgid "EE The \"{name}\" project has no associated path and is set as default" 110 | msgstr "" 111 | 112 | #: lib/App/Sqitch/GUI/Model.pm:121 113 | #, perl-brace-format 114 | msgid "EE Duplicate name found: \"{name}\"" 115 | msgstr "" 116 | 117 | #: lib/App/Sqitch/GUI/Model.pm:125 118 | #, perl-brace-format 119 | msgid "EE Duplicate path found: \"{path}\"" 120 | msgstr "" 121 | 122 | #: lib/App/Sqitch/GUI/Model.pm:214 lib/App/Sqitch/GUI/Model.pm:237 123 | #: lib/App/Sqitch/GUI/Model.pm:278 124 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:138 125 | msgid "Name" 126 | msgstr "" 127 | 128 | #: lib/App/Sqitch/GUI/Model.pm:220 lib/App/Sqitch/GUI/Model.pm:243 129 | msgid "Path" 130 | msgstr "" 131 | 132 | #: lib/App/Sqitch/GUI/Model.pm:249 133 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:193 134 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:401 135 | msgid "Engine" 136 | msgstr "" 137 | 138 | #: lib/App/Sqitch/GUI/Model.pm:255 lib/App/Sqitch/GUI/View/Panel/Project.pm:515 139 | msgid "Default" 140 | msgstr "" 141 | 142 | #: lib/App/Sqitch/GUI/Model.pm:261 lib/App/Sqitch/GUI/Model.pm:302 143 | msgid "Current" 144 | msgstr "" 145 | 146 | #: lib/App/Sqitch/GUI/Model.pm:284 147 | msgid "Create time" 148 | msgstr "" 149 | 150 | #: lib/App/Sqitch/GUI/Model.pm:290 151 | msgid "Creator" 152 | msgstr "" 153 | 154 | #: lib/App/Sqitch/GUI/Model.pm:296 155 | msgid "Description" 156 | msgstr "" 157 | 158 | #: lib/App/Sqitch/GUI/Model.pm:308 159 | msgid "Status" 160 | msgstr "" 161 | 162 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:162 163 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:366 164 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:469 165 | msgid "Project" 166 | msgstr "" 167 | 168 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:177 169 | msgid "Choose a directory" 170 | msgstr "" 171 | 172 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:227 173 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:371 174 | msgid "Database" 175 | msgstr "" 176 | 177 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:306 178 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:678 179 | msgid "&New" 180 | msgstr "" 181 | 182 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:326 183 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:701 184 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:741 185 | msgid "&Add" 186 | msgstr "" 187 | 188 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:346 189 | msgid "&Remove" 190 | msgstr "" 191 | 192 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:366 193 | msgid "&Close" 194 | msgstr "" 195 | 196 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:385 197 | msgid "&Save" 198 | msgstr "" 199 | 200 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:429 201 | msgid "Manage Projects List" 202 | msgstr "" 203 | 204 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:666 205 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:692 206 | msgid "C&ancel" 207 | msgstr "" 208 | 209 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:716 210 | msgid "Select an item, please." 211 | msgstr "" 212 | 213 | #: lib/App/Sqitch/GUI/View/Panel/Plan.pm:153 214 | msgid "Plan " 215 | msgstr "" 216 | 217 | #: lib/App/Sqitch/GUI/View/Panel/Plan.pm:168 218 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:489 219 | msgid "Load" 220 | msgstr "" 221 | 222 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:376 223 | msgid "User" 224 | msgstr "" 225 | 226 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:381 227 | msgid "URI" 228 | msgstr "" 229 | 230 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:386 231 | msgid "Created at" 232 | msgstr "" 233 | 234 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:391 235 | msgid "Creator name" 236 | msgstr "" 237 | 238 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:396 239 | msgid "Creator email" 240 | msgstr "" 241 | 242 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:406 243 | msgid "Repository" 244 | msgstr "" 245 | 246 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:502 247 | msgid "Initialize" 248 | msgstr "" 249 | 250 | #: lib/App/Sqitch/GUI/Wx/Menubar.pm:45 251 | msgid "&App" 252 | msgstr "" 253 | 254 | #: lib/App/Sqitch/GUI/Wx/Menubar.pm:46 255 | msgid "A&dmin" 256 | msgstr "" 257 | 258 | #: lib/App/Sqitch/GUI/Wx/Menubar.pm:47 259 | msgid "&Help" 260 | msgstr "" 261 | -------------------------------------------------------------------------------- /po/ro.po: -------------------------------------------------------------------------------- 1 | # Romanian translations for Sqitch-GUI package. 2 | # Copyright (C) 2013 "iovation Inc." 3 | # This file is distributed under the same license as the Sqitch-GUI package. 4 | # Automatically generated, 2013. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Sqitch-GUI 0.003\n" 9 | "Report-Msgid-Bugs-To: stefbv70@gmail.com\n" 10 | "POT-Creation-Date: 2018-06-10 09:24+0300\n" 11 | "PO-Revision-Date: 2018-06-10 09:34+0300\n" 12 | "Last-Translator: Ștefan Suciu \n" 13 | "Language-Team: none\n" 14 | "Language: ro\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " 19 | "20)) ? 1 : 2;\n" 20 | "X-Generator: Poedit 1.8.12\n" 21 | 22 | #: lib/App/Sqitch/GUI/Config.pm:138 23 | #, perl-brace-format 24 | msgid "The configuration file \"{file}\" does not exist!" 25 | msgstr "" 26 | 27 | #: lib/App/Sqitch/GUI/Config.pm:143 28 | #, perl-brace-format 29 | msgid "The configuration file \"{file}\" could not be (re)loaded: {err}" 30 | msgstr "" 31 | 32 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:24 33 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:27 34 | msgid "Projects" 35 | msgstr "Proiecte" 36 | 37 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:36 38 | msgid "Quit" 39 | msgstr "Ieșire" 40 | 41 | #: lib/App/Sqitch/GUI/Config/Toolbar.pm:39 42 | msgid "Quit the application" 43 | msgstr "Părăsește aplicația" 44 | 45 | #: lib/App/Sqitch/GUI/Controller.pm:70 46 | #, perl-brace-format 47 | msgid "EE Configuration error: \"{error}\"" 48 | msgstr "EE Eroare de configurare: \"{error}\"" 49 | 50 | #: lib/App/Sqitch/GUI/Controller.pm:91 51 | #, perl-brace-format 52 | msgid "EE Model error: \"{error}\"" 53 | msgstr "" 54 | 55 | #: lib/App/Sqitch/GUI/Controller.pm:268 56 | #, perl-brace-format 57 | msgid "II OK, found a project" 58 | msgid_plural "II Found {count} projects" 59 | msgstr[0] "" 60 | msgstr[1] "" 61 | msgstr[2] "" 62 | 63 | #: lib/App/Sqitch/GUI/Controller.pm:293 64 | msgid "II No valid default project, select the project and load it" 65 | msgid_plural "II No valid default project, select a project and load it" 66 | msgstr[0] "" 67 | msgstr[1] "" 68 | msgstr[2] "" 69 | 70 | #: lib/App/Sqitch/GUI/Controller.pm:306 lib/App/Sqitch/GUI/Controller.pm:475 71 | #: lib/App/Sqitch/GUI/Controller.pm:659 lib/App/Sqitch/GUI/Controller.pm:842 72 | msgid "Yes" 73 | msgstr "Da" 74 | 75 | #: lib/App/Sqitch/GUI/Controller.pm:323 76 | msgid "EE Something went wrong, no project \"name\" and path\"" 77 | msgstr "" 78 | 79 | #: lib/App/Sqitch/GUI/Controller.pm:338 80 | msgid "II Load plan item feature not implmented yet!" 81 | msgstr "" 82 | 83 | #: lib/App/Sqitch/GUI/Controller.pm:363 lib/App/Sqitch/GUI/Model.pm:130 84 | #, perl-brace-format 85 | msgid "EE The \"{path}\" path does not look like a Sqitch project!" 86 | msgstr "" 87 | 88 | #: lib/App/Sqitch/GUI/Controller.pm:371 89 | #, perl-brace-format 90 | msgid "II Can not cd to {path}\"" 91 | msgstr "" 92 | 93 | #: lib/App/Sqitch/GUI/Controller.pm:395 94 | #, perl-brace-format 95 | msgid "II Loading the \"{name}\" project" 96 | msgstr "II Încarc proiectul \"{name}\"" 97 | 98 | #: lib/App/Sqitch/GUI/Controller.pm:568 99 | msgid "II No changes defined yet" 100 | msgstr "II Încă nu sunt definite modificări" 101 | 102 | #: lib/App/Sqitch/GUI/Controller.pm:571 103 | msgid "II Loading changes..." 104 | msgstr "" 105 | 106 | #: lib/App/Sqitch/GUI/Controller.pm:792 107 | msgid "WW Select a project item, please" 108 | msgstr "WW Rog, selectați un articol" 109 | 110 | #: lib/App/Sqitch/GUI/Model.pm:94 111 | #, perl-brace-format 112 | msgid "EE The \"{name}\" project has no associated path and is set as default" 113 | msgstr "" 114 | 115 | #: lib/App/Sqitch/GUI/Model.pm:121 116 | #, perl-brace-format 117 | msgid "EE Duplicate name found: \"{name}\"" 118 | msgstr "" 119 | 120 | #: lib/App/Sqitch/GUI/Model.pm:125 121 | #, perl-brace-format 122 | msgid "EE Duplicate path found: \"{path}\"" 123 | msgstr "" 124 | 125 | #: lib/App/Sqitch/GUI/Model.pm:214 lib/App/Sqitch/GUI/Model.pm:237 126 | #: lib/App/Sqitch/GUI/Model.pm:278 127 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:138 128 | msgid "Name" 129 | msgstr "Nume" 130 | 131 | #: lib/App/Sqitch/GUI/Model.pm:220 lib/App/Sqitch/GUI/Model.pm:243 132 | msgid "Path" 133 | msgstr "Cale" 134 | 135 | #: lib/App/Sqitch/GUI/Model.pm:249 136 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:193 137 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:401 138 | msgid "Engine" 139 | msgstr "Motor" 140 | 141 | #: lib/App/Sqitch/GUI/Model.pm:255 lib/App/Sqitch/GUI/View/Panel/Project.pm:515 142 | msgid "Default" 143 | msgstr "Implicit" 144 | 145 | #: lib/App/Sqitch/GUI/Model.pm:261 lib/App/Sqitch/GUI/Model.pm:302 146 | msgid "Current" 147 | msgstr "Curent" 148 | 149 | #: lib/App/Sqitch/GUI/Model.pm:284 150 | msgid "Create time" 151 | msgstr "Creat la" 152 | 153 | #: lib/App/Sqitch/GUI/Model.pm:290 154 | msgid "Creator" 155 | msgstr "Nume creator" 156 | 157 | #: lib/App/Sqitch/GUI/Model.pm:296 158 | msgid "Description" 159 | msgstr "Descriere" 160 | 161 | #: lib/App/Sqitch/GUI/Model.pm:308 162 | msgid "Status" 163 | msgstr "Stare" 164 | 165 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:162 166 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:366 167 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:469 168 | msgid "Project" 169 | msgstr "Proiect" 170 | 171 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:177 172 | msgid "Choose a directory" 173 | msgstr "Alege un subdirector" 174 | 175 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:227 176 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:371 177 | msgid "Database" 178 | msgstr "Bază de date" 179 | 180 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:306 181 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:678 182 | msgid "&New" 183 | msgstr "&Nou" 184 | 185 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:326 186 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:701 187 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:741 188 | msgid "&Add" 189 | msgstr "&Adaugă" 190 | 191 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:346 192 | msgid "&Remove" 193 | msgstr "Ș&terge" 194 | 195 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:366 196 | msgid "&Close" 197 | msgstr "Închide" 198 | 199 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:385 200 | msgid "&Save" 201 | msgstr "Salvează" 202 | 203 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:429 204 | msgid "Manage Projects List" 205 | msgstr "" 206 | 207 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:666 208 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:692 209 | msgid "C&ancel" 210 | msgstr "Renunț" 211 | 212 | #: lib/App/Sqitch/GUI/View/Dialog/Projects.pm:716 213 | msgid "Select an item, please." 214 | msgstr "Rog, selectați un articol." 215 | 216 | #: lib/App/Sqitch/GUI/View/Panel/Plan.pm:153 217 | msgid "Plan " 218 | msgstr "Plan" 219 | 220 | #: lib/App/Sqitch/GUI/View/Panel/Plan.pm:168 221 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:489 222 | msgid "Load" 223 | msgstr "Încarcă" 224 | 225 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:376 226 | msgid "User" 227 | msgstr "Utilizator" 228 | 229 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:381 230 | msgid "URI" 231 | msgstr "URI" 232 | 233 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:386 234 | msgid "Created at" 235 | msgstr "Creat la" 236 | 237 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:391 238 | msgid "Creator name" 239 | msgstr "Nume creator" 240 | 241 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:396 242 | msgid "Creator email" 243 | msgstr "Email creator" 244 | 245 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:406 246 | msgid "Repository" 247 | msgstr "Depozit" 248 | 249 | #: lib/App/Sqitch/GUI/View/Panel/Project.pm:502 250 | msgid "Initialize" 251 | msgstr "Inițializează" 252 | 253 | #: lib/App/Sqitch/GUI/Wx/Menubar.pm:45 254 | msgid "&App" 255 | msgstr "&App" 256 | 257 | #: lib/App/Sqitch/GUI/Wx/Menubar.pm:46 258 | msgid "A&dmin" 259 | msgstr "A&dministrare" 260 | 261 | #: lib/App/Sqitch/GUI/Wx/Menubar.pm:47 262 | msgid "&Help" 263 | msgstr "A&jutor" 264 | 265 | #~ msgid "This should NOT happen!" 266 | #~ msgstr "Acesta NU ar fi trebuit să se întâmple!" 267 | 268 | #, fuzzy 269 | #~ msgid "Wrong arguments passed to load_txt_form_for()" 270 | #~ msgstr "Argumente eronate pentru _set_default_mark()" 271 | 272 | #~ msgid "Sqitch is NOT initialized yet. Please set a valid repository path!" 273 | #~ msgstr "" 274 | #~ "Sqitch NU este încă inițializat. Rog, setați o cale către un depozit " 275 | #~ "valid!" 276 | 277 | #~ msgid "Sqitch is initialized." 278 | #~ msgstr "Sqitch este inițializat." 279 | 280 | #~ msgid "Error: " 281 | #~ msgstr "Eroare:" 282 | 283 | #~ msgid "Driver" 284 | #~ msgstr "Driver" 285 | 286 | #~ msgid "Add" 287 | #~ msgstr "Adaug" 288 | 289 | #~ msgid "Repository List" 290 | #~ msgstr "Listă Depozite" 291 | 292 | #~ msgid "&Load" 293 | #~ msgstr "Î&ncarcă" 294 | 295 | #~ msgid "&Default" 296 | #~ msgstr "Implicit" 297 | 298 | #~ msgid "&Edit" 299 | #~ msgstr "&Editează" 300 | 301 | #~ msgid "Wrong arguments passed to _control_write_p()" 302 | #~ msgstr "Argumente eronate pentru _control_write_p()" 303 | 304 | #~ msgid "Wrong arguments passed to _control_write_e()" 305 | #~ msgstr "Argumente eronate pentru _control_write_e()" 306 | 307 | #~ msgid "Wrong arguments passed to _control_write_c()" 308 | #~ msgstr "Argumente eronate pentru _control_write_p()" 309 | 310 | #~ msgid "Wrong arguments passed to _control_read_e()" 311 | #~ msgstr "Argumente eronate pentru _control_read_e()" 312 | 313 | #~ msgid "Wrong arguments passed to _control_read_p()" 314 | #~ msgstr "Argumente eronate pentru _control_read_p()" 315 | 316 | #~ msgid "" 317 | #~ "Duplicate! To add a new repository, select a new path and add a name for " 318 | #~ "it." 319 | #~ msgstr "" 320 | #~ "Duplicat! Ca să adaugați un depozit, selectați o cale şi dați-i un nume." 321 | -------------------------------------------------------------------------------- /share/etc/icons/README: -------------------------------------------------------------------------------- 1 | 2 | The icons are from the Tango theme. 3 | -------------------------------------------------------------------------------- /share/etc/icons/preferences-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqitchers/sqitch-gui/31905dd0f21bd8d7a69c18d3af060d605ce81160/share/etc/icons/preferences-desktop.png -------------------------------------------------------------------------------- /share/etc/icons/preferences-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqitchers/sqitch-gui/31905dd0f21bd8d7a69c18d3af060d605ce81160/share/etc/icons/preferences-system.png -------------------------------------------------------------------------------- /share/etc/icons/system-log-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqitchers/sqitch-gui/31905dd0f21bd8d7a69c18d3af060d605ce81160/share/etc/icons/system-log-out.png -------------------------------------------------------------------------------- /t/00-load.t: -------------------------------------------------------------------------------- 1 | #!perl -T 2 | 3 | use 5.010; 4 | use strict; 5 | use warnings FATAL => 'all'; 6 | use Test::More; 7 | 8 | plan tests => 1; 9 | 10 | BEGIN { 11 | use_ok( 'App::Sqitch::GUI' ) || print "Bail out!\n"; 12 | } 13 | 14 | diag( "Testing App::Sqitch::GUI with Perl $], $^X" ); 15 | -------------------------------------------------------------------------------- /t/config.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Path::Class qw(dir file); 5 | 6 | use App::Sqitch::GUI::Config; 7 | 8 | $ENV{HOME} = dir('t', 'home')->stringify; # set HOME for testing 9 | 10 | # protect against user's environment variables (from Sqitch) 11 | delete @ENV{qw( SQITCH_CONFIG SQITCH_USER_CONFIG SQITCH_SYSTEM_CONFIG )}; 12 | 13 | ok my $conf = App::Sqitch::GUI::Config->new, 'new config instance'; 14 | 15 | is $conf->user_dir, dir( 't', 'home', '.sqitch' )->stringify, 'user config dir'; 16 | 17 | ok $conf->current_project_path( dir( 't', 'home', 'flipr' ) ), 18 | 'set current path'; 19 | 20 | is $conf->confname, 'sqitch.conf', 'config file name'; 21 | is $conf->user_file, 22 | file( 't', 'home', '.sqitch', 'sqitch.conf' )->stringify, 23 | 'user config file'; 24 | is $conf->local_file, file( 't', 'home', 'flipr', 'sqitch.conf' )->stringify, 25 | 'local config file'; 26 | 27 | my ( $name, $path ) = ( 'flipr', dir( 't', 'home', 'flipr' )->stringify ); 28 | 29 | my $conf_href = { "project.${name}.path" => $path }; 30 | 31 | is_deeply $conf->_conf_projects_list, $conf_href, 'projects config'; 32 | is_deeply $conf->project_list, { $name => $path }, 'projects list'; 33 | 34 | is $conf->default_project_name, $name, 'default repo name'; 35 | is $conf->default_project_path, $path, 'default repo path'; 36 | 37 | # Inspired from: 38 | # http://perltricks.com/article/178/2015/6/17/Separate-data-and-behavior-with-table-driven-testing 39 | # Thanks! 40 | my @data = ( 41 | [ ( 'unknown', 'Unknown' ) ], 42 | [ ( 'pg', 'PostgreSQL' ) ], 43 | [ ( 'mysql', 'MySQL' ) ], 44 | [ ( 'sqlite', 'SQLite' ) ], 45 | [ ( 'oracle', 'Oracle' ) ], 46 | [ ( 'firebird', 'Firebird' ) ], 47 | ); 48 | foreach my $row ( @data ) { 49 | is $conf->get_engine_name($row->[0]), $row->[1], 50 | "engine name: $row->[1]"; 51 | } 52 | 53 | # Not used, yet 54 | # $conf->get_engine_from_name 55 | # $conf->has_repo_name 56 | # $conf->has_repo_path 57 | 58 | is $conf->project_list_cnt, 1, 'project count'; 59 | 60 | like $conf->icon_path, qr/share.+icons/, 'get the icons path'; 61 | 62 | done_testing; 63 | -------------------------------------------------------------------------------- /t/home/.sqitch/sqitch.conf: -------------------------------------------------------------------------------- 1 | [user] 2 | name = Marge N. O'Vera 3 | email = marge@example.com 4 | [core] 5 | project = flipr 6 | [project "flipr"] 7 | path = t/home/flipr 8 | -------------------------------------------------------------------------------- /t/home/flipr/deploy/users.sql: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | CREATE SCHEMA __myapp; 3 | CREATE TABLE __myapp.users ( 4 | nick TEXT PRIMARY KEY, 5 | name TEXT NOT NULL 6 | ); 7 | -------------------------------------------------------------------------------- /t/home/flipr/revert/users.sql: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | DROP SCHEMA IF EXISTS __myapp CASCADE; 3 | -------------------------------------------------------------------------------- /t/home/flipr/sqitch.conf: -------------------------------------------------------------------------------- 1 | [core] 2 | engine = pg 3 | [deploy] 4 | verify = true 5 | [rebase] 6 | verify = true 7 | [engine "pg"] 8 | target = flipr_test 9 | [target "flipr_test"] 10 | uri = db:pg:flipr_test 11 | -------------------------------------------------------------------------------- /t/home/flipr/sqitch.plan: -------------------------------------------------------------------------------- 1 | %project=engine 2 | 3 | + users 2012-07-16T17:25:07Z Barack Obama # User roles 4 | -------------------------------------------------------------------------------- /t/home/flipr/verify/users.sql: -------------------------------------------------------------------------------- 1 | SELECT nick, name FROM __myapp.users WHERE FALSE; 2 | -------------------------------------------------------------------------------- /t/home/intro/README.md: -------------------------------------------------------------------------------- 1 | Sqitch Intro v1.0.0-b6 2 | ====================== 3 | 4 | A tutorial repository for [Sqitch](http://sqitch.org/). The actual tutorial is 5 | [`sqitchtutorial`](https://sqitch.org/docs/manual/sqitchtutorial/); this 6 | repository is actually the example repository described in the tutorial. So you 7 | may want to look at the history here. Or clone this repository and run `sqitch` 8 | against various checkouts or just against the Git history. 9 | 10 | 11 | Author 12 | ====== 13 | 14 | David E. Wheeler 15 | 16 | License 17 | ======= 18 | 19 | Copyright (c) 2012-2013 iovation Inc. 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | -------------------------------------------------------------------------------- /t/home/intro/deploy/appschema.sql: -------------------------------------------------------------------------------- 1 | -- Deploy appschema 2 | 3 | BEGIN; 4 | 5 | CREATE SCHEMA flipr; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/deploy/change_pass.sql: -------------------------------------------------------------------------------- 1 | -- Deploy change_pass 2 | -- requires: users 3 | -- requires: appschema 4 | -- requires: pgcrypto 5 | 6 | BEGIN; 7 | 8 | CREATE OR REPLACE FUNCTION flipr.change_pass( 9 | nick TEXT, 10 | oldpass TEXT, 11 | newpass TEXT 12 | ) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$ 13 | BEGIN 14 | UPDATE flipr.users 15 | SET password = crypt($3, gen_salt('md5')) 16 | WHERE nickname = $1 17 | AND password = crypt($2, password); 18 | RETURN FOUND; 19 | END; 20 | $$; 21 | 22 | COMMIT; 23 | -------------------------------------------------------------------------------- /t/home/intro/deploy/change_pass@v1.0.0-dev2.sql: -------------------------------------------------------------------------------- 1 | -- Deploy change_pass 2 | -- requires: users 3 | -- requires: appschema 4 | 5 | BEGIN; 6 | 7 | CREATE OR REPLACE FUNCTION flipr.change_pass( 8 | nick TEXT, 9 | oldpass TEXT, 10 | newpass TEXT 11 | ) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$ 12 | BEGIN 13 | UPDATE flipr.users 14 | SET password = md5($3) 15 | WHERE nickname = $1 16 | AND password = md5($2); 17 | RETURN FOUND; 18 | END; 19 | $$; 20 | 21 | COMMIT; 22 | -------------------------------------------------------------------------------- /t/home/intro/deploy/delete_flip.sql: -------------------------------------------------------------------------------- 1 | -- Deploy delete_flip 2 | -- requires: flips 3 | -- requires: appschema 4 | -- requires: users 5 | 6 | BEGIN; 7 | 8 | CREATE OR REPLACE FUNCTION flipr.delete_flip( 9 | flip_id BIGINT 10 | ) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$ 11 | BEGIN 12 | DELETE FROM flipe.flips WHERE id = flip_id; 13 | RETURN FOUND; 14 | END; 15 | $$; 16 | 17 | COMMIT; 18 | -------------------------------------------------------------------------------- /t/home/intro/deploy/delete_list.sql: -------------------------------------------------------------------------------- 1 | -- Deploy delete_list 2 | -- requires: lists 3 | -- requires: appschema 4 | -- requires: users 5 | 6 | BEGIN; 7 | 8 | CREATE OR REPLACE FUNCTION flipr.delete_list( 9 | nickname TEXT, 10 | name TEXT 11 | ) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$ 12 | BEGIN 13 | DELETE FROM flipr.lists 14 | WHERE nickname = $1 15 | AND name = $2; 16 | RETURN FOUND; 17 | END; 18 | $$; 19 | 20 | COMMIT; 21 | -------------------------------------------------------------------------------- /t/home/intro/deploy/flips.sql: -------------------------------------------------------------------------------- 1 | -- Deploy flips 2 | -- requires: appschema 3 | -- requires: users 4 | 5 | BEGIN; 6 | 7 | SET client_min_messages = 'warning'; 8 | 9 | CREATE TABLE flipr.flips ( 10 | id BIGSERIAL PRIMARY KEY, 11 | nickname TEXT NOT NULL REFERENCES flipr.users(nickname), 12 | body TEXT NOT NULL DEFAULT '' CHECK ( length(body) <= 180 ), 13 | timestamp TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp() 14 | ); 15 | 16 | COMMIT; 17 | -------------------------------------------------------------------------------- /t/home/intro/deploy/insert_flip.sql: -------------------------------------------------------------------------------- 1 | -- Deploy insert_flip 2 | -- requires: flips 3 | -- requires: appschema 4 | -- requires: users 5 | 6 | BEGIN; 7 | 8 | CREATE OR REPLACE FUNCTION flipr.insert_flip( 9 | nickname TEXT, 10 | body TEXT 11 | ) RETURNS BIGINT LANGUAGE sql SECURITY DEFINER AS $$ 12 | INSERT INTO flipr.flips (nickname, body) 13 | VALUES ($1, $2) 14 | RETURNING id; 15 | $$; 16 | 17 | COMMIT; 18 | -------------------------------------------------------------------------------- /t/home/intro/deploy/insert_list.sql: -------------------------------------------------------------------------------- 1 | -- Deploy insert_list 2 | -- requires: lists 3 | -- requires: appschema 4 | -- requires: users 5 | 6 | BEGIN; 7 | 8 | CREATE OR REPLACE FUNCTION flipr.insert_list( 9 | nickname TEXT, 10 | name TEXT, 11 | description TEXT 12 | ) RETURNS VOID LANGUAGE sql SECURITY DEFINER AS $$ 13 | INSERT INTO flipr.lists (nickname, name, description) 14 | VALUES ($1, $2, $3); 15 | $$; 16 | 17 | COMMIT; 18 | -------------------------------------------------------------------------------- /t/home/intro/deploy/insert_user.sql: -------------------------------------------------------------------------------- 1 | -- Deploy insert_user 2 | -- requires: users 3 | -- requires: appschema 4 | -- requires: pgcrypto 5 | 6 | BEGIN; 7 | 8 | CREATE OR REPLACE FUNCTION flipr.insert_user( 9 | nickname TEXT, 10 | password TEXT 11 | ) RETURNS VOID LANGUAGE SQL SECURITY DEFINER AS $$ 12 | INSERT INTO flipr.users values($1, crypt($2, gen_salt('md5'))); 13 | $$; 14 | 15 | COMMIT; 16 | -------------------------------------------------------------------------------- /t/home/intro/deploy/insert_user@v1.0.0-dev2.sql: -------------------------------------------------------------------------------- 1 | -- Deploy insert_user 2 | -- requires: users 3 | -- requires: appschema 4 | 5 | BEGIN; 6 | 7 | CREATE OR REPLACE FUNCTION flipr.insert_user( 8 | nickname TEXT, 9 | password TEXT 10 | ) RETURNS VOID LANGUAGE SQL SECURITY DEFINER AS $$ 11 | INSERT INTO flipr.users VALUES($1, md5($2)); 12 | $$; 13 | 14 | COMMIT; 15 | -------------------------------------------------------------------------------- /t/home/intro/deploy/lists.sql: -------------------------------------------------------------------------------- 1 | -- Deploy lists 2 | -- requires: appschema 3 | -- requires: users 4 | 5 | BEGIN; 6 | 7 | SET client_min_messages = 'warning'; 8 | 9 | CREATE TABLE flipr.lists ( 10 | nickname TEXT NOT NULL REFERENCES flipr.users(nickname), 11 | name TEXT NOT NULL DEFAULT '', 12 | description TEXT NOT NULL DEFAULT '', 13 | created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp() 14 | ); 15 | 16 | COMMIT; 17 | -------------------------------------------------------------------------------- /t/home/intro/deploy/pgcrypto.sql: -------------------------------------------------------------------------------- 1 | -- Deploy pgcrypto 2 | 3 | BEGIN; 4 | 5 | CREATE EXTENSION pgcrypto; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/deploy/users.sql: -------------------------------------------------------------------------------- 1 | -- Deploy users 2 | -- requires: appschema 3 | 4 | BEGIN; 5 | 6 | SET client_min_messages = 'warning'; 7 | 8 | CREATE TABLE flipr.users ( 9 | nickname TEXT PRIMARY KEY, 10 | password TEXT NOT NULL, 11 | timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW() 12 | ); 13 | 14 | COMMIT; 15 | -------------------------------------------------------------------------------- /t/home/intro/revert/appschema.sql: -------------------------------------------------------------------------------- 1 | -- Revert appschema 2 | 3 | BEGIN; 4 | 5 | DROP SCHEMA flipr; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/change_pass.sql: -------------------------------------------------------------------------------- 1 | -- Deploy change_pass 2 | -- requires: users 3 | -- requires: appschema 4 | 5 | BEGIN; 6 | 7 | CREATE OR REPLACE FUNCTION flipr.change_pass( 8 | nick TEXT, 9 | oldpass TEXT, 10 | newpass TEXT 11 | ) RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$ 12 | BEGIN 13 | UPDATE flipr.users 14 | SET password = md5($3) 15 | WHERE nickname = $1 16 | AND password = md5($2); 17 | RETURN FOUND; 18 | END; 19 | $$; 20 | 21 | COMMIT; 22 | -------------------------------------------------------------------------------- /t/home/intro/revert/change_pass@v1.0.0-dev2.sql: -------------------------------------------------------------------------------- 1 | -- Revert change_pass 2 | 3 | BEGIN; 4 | 5 | DROP FUNCTION flipr.change_pass(TEXT, TEXT, TEXT); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/delete_flip.sql: -------------------------------------------------------------------------------- 1 | -- Revert delete_flip 2 | 3 | BEGIN; 4 | 5 | DROP FUNCTION flipr.delete_flip(BIGINT); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/delete_list.sql: -------------------------------------------------------------------------------- 1 | -- Revert delete_list 2 | 3 | BEGIN; 4 | 5 | DROP FUNCTION flipr.delete_list(TEXT, TEXT); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/flips.sql: -------------------------------------------------------------------------------- 1 | -- Revert flips 2 | 3 | BEGIN; 4 | 5 | DROP TABLE flipr.flips; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/insert_flip.sql: -------------------------------------------------------------------------------- 1 | -- Revert insert_flip 2 | 3 | BEGIN; 4 | 5 | DROP FUNCTION flipr.insert_flip(TEXT, TEXT); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/insert_list.sql: -------------------------------------------------------------------------------- 1 | -- Revert insert_list 2 | 3 | BEGIN; 4 | 5 | DROP FUNCTION flipr.insert_list(TEXT, TEXT, TEXT); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/insert_user.sql: -------------------------------------------------------------------------------- 1 | -- Deploy insert_user 2 | -- requires: users 3 | -- requires: appschema 4 | 5 | BEGIN; 6 | 7 | CREATE OR REPLACE FUNCTION flipr.insert_user( 8 | nickname TEXT, 9 | password TEXT 10 | ) RETURNS VOID LANGUAGE SQL SECURITY DEFINER AS $$ 11 | INSERT INTO flipr.users VALUES($1, md5($2)); 12 | $$; 13 | 14 | COMMIT; 15 | -------------------------------------------------------------------------------- /t/home/intro/revert/insert_user@v1.0.0-dev2.sql: -------------------------------------------------------------------------------- 1 | -- Revert insert_user 2 | 3 | BEGIN; 4 | 5 | DROP FUNCTION flipr.insert_user(TEXT, TEXT); 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/lists.sql: -------------------------------------------------------------------------------- 1 | -- Revert lists 2 | 3 | BEGIN; 4 | 5 | DROP TABLE flipr.lists; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/pgcrypto.sql: -------------------------------------------------------------------------------- 1 | -- Revert pgcrypto 2 | 3 | BEGIN; 4 | 5 | DROP EXTENSION pgcrypto; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/revert/users.sql: -------------------------------------------------------------------------------- 1 | -- Revert users 2 | 3 | BEGIN; 4 | 5 | DROP TABLE flipr.users; 6 | 7 | COMMIT; 8 | -------------------------------------------------------------------------------- /t/home/intro/sqitch.conf: -------------------------------------------------------------------------------- 1 | [core] 2 | engine = pg 3 | # plan_file = sqitch.plan 4 | # top_dir = . 5 | # deploy_dir = deploy 6 | # revert_dir = revert 7 | # verify_dir = verify 8 | # extension = sql 9 | # [core "pg"] 10 | # target = db:pg: 11 | # registry = sqitch 12 | # client = /usr/local/pgsql/bin/psql 13 | [user] 14 | name = Marge N. O’Vera 15 | email = marge@example.com 16 | [target "flipr_test"] 17 | uri = db:pg:flipr_test 18 | [deploy] 19 | verify = true 20 | [rebase] 21 | verify = true 22 | [engine "pg"] 23 | target = flipr_test 24 | -------------------------------------------------------------------------------- /t/home/intro/sqitch.plan: -------------------------------------------------------------------------------- 1 | %syntax-version=1.0.0-b2 2 | %project=flipr 3 | %uri=https://github.com/theory/sqitch-intro/ 4 | 5 | appschema 2013-12-30T23:19:45Z Marge N. O’Vera # Add schema for all flipr objects. 6 | users [appschema] 2013-12-30T23:49:00Z Marge N. O’Vera # Creates table to track our users. 7 | insert_user [users appschema] 2013-12-30T23:57:36Z Marge N. O’Vera # Creates a function to insert a user. 8 | change_pass [users appschema] 2013-12-30T23:57:45Z Marge N. O’Vera # Creates a function to change a user password. 9 | @v1.0.0-dev1 2013-12-31T00:01:22Z Marge N. O’Vera # Tag v1.0.0-dev1. 10 | 11 | lists [appschema users] 2013-12-31T00:39:40Z Marge N. O’Vera # Adds table for storing lists. 12 | insert_list [lists appschema users] 2013-12-31T00:41:29Z Marge N. O’Vera # Creates a function to insert a list. 13 | delete_list [lists appschema users] 2013-12-31T00:41:37Z Marge N. O’Vera # Creates a function to delete a list. 14 | flips [appschema users] 2013-12-31T00:32:39Z Marge N. O’Vera # Adds table for storing flips. 15 | insert_flip [flips appschema users] 2013-12-31T00:35:59Z Marge N. O’Vera # Creates a function to insert a flip. 16 | delete_flip [flips appschema users] 2013-12-31T00:36:34Z Marge N. O’Vera # Creates a function to delete a flip. 17 | @v1.0.0-dev2 2013-12-31T00:55:06Z Marge N. O’Vera # Tag v1.0.0-dev2. 18 | 19 | pgcrypto 2013-12-31T00:57:24Z Marge N. O’Vera # Loads pgcrypto extension. 20 | insert_user [insert_user@v1.0.0-dev2 pgcrypto] 2013-12-31T01:00:10Z Marge N. O’Vera # Change insert_user to use pgcrypto. 21 | change_pass [change_pass@v1.0.0-dev2 pgcrypto] 2013-12-31T01:02:35Z Marge N. O’Vera # Change change_pass to use pgcrypto. 22 | -------------------------------------------------------------------------------- /t/home/intro/verify/appschema.sql: -------------------------------------------------------------------------------- 1 | -- Verify appschema 2 | 3 | BEGIN; 4 | 5 | SELECT pg_catalog.has_schema_privilege('flipr', 'usage'); 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /t/home/intro/verify/change_pass.sql: -------------------------------------------------------------------------------- 1 | -- Verify change_pass 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.change_pass(text, text, text)', 'execute'); 6 | 7 | SELECT 1/COUNT(*) 8 | FROM pg_catalog.pg_proc 9 | WHERE proname = 'change_pass' 10 | AND pg_get_functiondef(oid) LIKE $$%crypt($3, gen_salt('md5'))%$$; 11 | 12 | COMMIT; 13 | -------------------------------------------------------------------------------- /t/home/intro/verify/change_pass@v1.0.0-dev2.sql: -------------------------------------------------------------------------------- 1 | -- Verify change_pass 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.change_pass(text, text, text)', 'execute'); 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /t/home/intro/verify/delete_flip.sql: -------------------------------------------------------------------------------- 1 | -- Verify delete_flip 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.delete_flip(bigint)', 'execute'); 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /t/home/intro/verify/delete_list.sql: -------------------------------------------------------------------------------- 1 | -- Verify delete_list 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.delete_list(text, text)', 'execute'); 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /t/home/intro/verify/flips.sql: -------------------------------------------------------------------------------- 1 | -- Verify flips 2 | 3 | BEGIN; 4 | 5 | SELECT id 6 | , nickname 7 | , body 8 | , timestamp 9 | FROM flipr.flips 10 | WHERE FALSE; 11 | 12 | ROLLBACK; 13 | -------------------------------------------------------------------------------- /t/home/intro/verify/insert_flip.sql: -------------------------------------------------------------------------------- 1 | -- Verify insert_flip 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.insert_flip(text, text)', 'execute'); 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /t/home/intro/verify/insert_list.sql: -------------------------------------------------------------------------------- 1 | -- Verify insert_list 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.insert_list(text, text, text)', 'execute'); 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /t/home/intro/verify/insert_user.sql: -------------------------------------------------------------------------------- 1 | -- Verify insert_user 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.insert_user(text, text)', 'execute'); 6 | 7 | SELECT 1/COUNT(*) 8 | FROM pg_catalog.pg_proc 9 | WHERE proname = 'insert_user' 10 | AND pg_get_functiondef(oid) LIKE $$%crypt($2, gen_salt('md5'))%$$; 11 | 12 | COMMIT; 13 | -------------------------------------------------------------------------------- /t/home/intro/verify/insert_user@v1.0.0-dev2.sql: -------------------------------------------------------------------------------- 1 | -- Verify insert_user 2 | 3 | BEGIN; 4 | 5 | SELECT has_function_privilege('flipr.insert_user(text, text)', 'execute'); 6 | 7 | ROLLBACK; 8 | -------------------------------------------------------------------------------- /t/home/intro/verify/lists.sql: -------------------------------------------------------------------------------- 1 | -- Verify lists 2 | 3 | BEGIN; 4 | 5 | SELECT nickname, name, description, created_at 6 | FROM flipr.lists 7 | WHERE FALSE; 8 | 9 | ROLLBACK; 10 | -------------------------------------------------------------------------------- /t/home/intro/verify/pgcrypto.sql: -------------------------------------------------------------------------------- 1 | -- Verify pgcrypto 2 | 3 | BEGIN; 4 | 5 | SELECT 1/count(*) FROM pg_extension WHERE extname = 'pgcrypto'; 6 | SELECT has_function_privilege('crypt(text, text)', 'execute'); 7 | SELECT has_function_privilege('gen_salt(text)', 'execute'); 8 | 9 | ROLLBACK; 10 | -------------------------------------------------------------------------------- /t/home/intro/verify/users.sql: -------------------------------------------------------------------------------- 1 | -- Verify users 2 | 3 | BEGIN; 4 | 5 | SELECT nickname, password, timestamp 6 | FROM flipr.users 7 | WHERE FALSE; 8 | 9 | COMMIT; 10 | -------------------------------------------------------------------------------- /t/model-datatable.t: -------------------------------------------------------------------------------- 1 | # 2 | # Testing App::Sqitch::GUI::Model::ListDataTable 3 | # 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use Test::Most; 9 | 10 | use lib qw( lib ../lib ); 11 | 12 | use App::Sqitch::GUI::Model::ListDataTable; 13 | 14 | ok my $dt = App::Sqitch::GUI::Model::ListDataTable->new, 'new data table'; 15 | 16 | is $dt->get_item_count, 0, 'item count: no values'; 17 | 18 | ok $dt->add_row('Item1', 'Data1'), 'add row 1'; 19 | is $dt->get_item_count, 1, 'item count: 1 value'; 20 | is $dt->get_value( 0, 0 ), 'Item1', 'get value'; 21 | is $dt->get_value( 0, 1 ), 'Data1', 'get value'; 22 | 23 | ok $dt->add_row('Item2', 'Data2'), 'add row 2'; 24 | is $dt->get_item_count, 2, 'item count: 2 value'; 25 | is $dt->get_value( 1, 0 ), 'Item2', 'get value'; 26 | is $dt->get_value( 1, 1 ), 'Data2', 'get value'; 27 | 28 | ok $dt->add_row('Item3', 'Data3'), 'add row 2'; 29 | is $dt->get_item_count, 3, 'item count: 3 values'; 30 | is $dt->get_value( 2, 0 ), 'Item3', 'get value'; 31 | is $dt->get_value( 2, 1 ), 'Data3', 'get value'; 32 | 33 | ok $dt->set_value( 2, 0, 'Item3e'), 'change value'; 34 | ok $dt->set_value( 2, 1, 'Data3e'), 'change value'; 35 | is $dt->get_value( 2, 0 ), 'Item3e', 'get value'; 36 | is $dt->get_value( 2, 1 ), 'Data3e', 'get value'; 37 | is $dt->get_item_count, 3, 'item count: 3 values'; 38 | #diag $dt->get_data_as_string; 39 | 40 | ok $dt->remove_row(1), 'remove row 1'; 41 | is $dt->get_item_count, 2, 'item count: 2 values'; 42 | #diag $dt->get_data_as_string; 43 | 44 | # Change entire col 0 to "Item" 45 | ok $dt->set_col(0, 'Item'), 'Set col 0 to "Item"'; 46 | is $dt->get_value( 0, 0 ), 'Item', 'get value'; 47 | is $dt->get_value( 1, 0 ), 'Item', 'get value'; 48 | 49 | # Change entire col 0 to "Item1, Item3" 50 | ok $dt->set_col(0, ['Item1', 'Item3']), 'Set col 0 to "Item"'; 51 | is $dt->get_value( 0, 0 ), 'Item1', 'get value'; 52 | is $dt->get_value( 1, 0 ), 'Item3', 'get value'; 53 | #diag $dt->get_data_as_string; 54 | 55 | # Test for failure: 56 | throws_ok { $dt->get_value( 2, 0 ) } 57 | qr/\Qno such row/, 58 | 'Should get an exception for an inexistent row - get_value'; 59 | 60 | throws_ok { $dt->set_value( 2, 0 ) } 61 | qr/\Qno such row/, 62 | 'Should get an exception for an inexistent row - set_value'; 63 | 64 | done_testing; 65 | -------------------------------------------------------------------------------- /t/model-projectitem.t: -------------------------------------------------------------------------------- 1 | # 2 | # Testing App::Sqitch::GUI::Model::ProjectItem 3 | # 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use Test::More; 9 | use lib qw( lib ../lib ); 10 | 11 | use Path::Class; 12 | use App::Sqitch::GUI::Model::ProjectItem; 13 | 14 | ok my $project = App::Sqitch::GUI::Model::ProjectItem->new, 15 | 'new project item'; 16 | 17 | is $project->item, undef, 'project item'; 18 | is $project->name, undef, 'project name'; 19 | is $project->path, undef, 'project path'; 20 | 21 | my $test_path = dir( 't', 'home', 'test-repo' ); 22 | my $test2path = dir( 't', 'home', 'another-repo' ); 23 | 24 | ok !$project->item(0), 'set project item'; 25 | ok $project->name('flipr'), 'set project name'; 26 | ok $project->path($test_path), 'set project path'; 27 | 28 | is $project->item, 0, 'project item'; 29 | is $project->name, 'flipr', 'project name'; 30 | is $project->path, $test_path->stringify, 'project path'; 31 | 32 | ok $project->item(1), 'set project item'; 33 | ok $project->name('flipr2'), 'set project name'; 34 | ok $project->path($test2path), 'set project path'; 35 | 36 | is $project->item, 1, 'project item'; 37 | is $project->name, 'flipr2', 'project name'; 38 | is $project->path, $test2path->stringify, 'project path'; 39 | 40 | done_testing; 41 | -------------------------------------------------------------------------------- /t/model.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test the Model 3 | # 4 | use 5.010; 5 | use strict; 6 | use warnings; 7 | use Test::More; 8 | use Path::Class qw(dir file); 9 | 10 | use App::Sqitch::GUI::Config; 11 | use App::Sqitch::GUI::Model; 12 | 13 | $ENV{HOME} = dir('t', 'home')->stringify; # set HOME for testing 14 | 15 | # protect against user's environment variables (from Sqitch) 16 | delete @ENV{qw( SQITCH_CONFIG SQITCH_USER_CONFIG SQITCH_SYSTEM_CONFIG )}; 17 | 18 | ok my $conf = App::Sqitch::GUI::Config->new, 'new config instance'; 19 | isa_ok $conf, 'App::Sqitch::GUI::Config', 'GUI::Config'; 20 | 21 | my ( $name, $path ) = ( 'flipr', dir( 't', 'home', 'flipr' ) ); 22 | 23 | is $conf->default_project_name, $name, 'default project name'; 24 | is $conf->default_project_path, $path->stringify, 'default project path'; 25 | 26 | ok my $model = App::Sqitch::GUI::Model->new( config => $conf ), 27 | 'new model instance'; 28 | isa_ok $model, 'App::Sqitch::GUI::Model', 'GUI::Model'; 29 | 30 | for my $rec ( $model->projects ) { 31 | my ($name, $attrib) = @{$rec}; 32 | ok $name, "project '$name'"; 33 | is ref $attrib, 'HASH', 'project attributes'; 34 | } 35 | 36 | isa_ok $model->project_list_data, 'App::Sqitch::GUI::Model::ListDataTable', 37 | 'Project ListDataTable'; 38 | isa_ok $model->plan_list_data, 'App::Sqitch::GUI::Model::ListDataTable', 39 | 'Plan ListDataTable'; 40 | 41 | is ref $model->project_dlg_list_meta_data, 'ARRAY', 'project dlg list metadata'; 42 | is ref $model->project_list_meta_data, 'ARRAY', 'project list metadata'; 43 | is ref $model->plan_list_meta_data, 'ARRAY', 'plan list metadata'; 44 | 45 | ( $name, $path ) = ( 'flipr', dir( 't', 'home', 'intro' ) ); 46 | 47 | ok $conf->default_project_name($name), 'set default project name'; 48 | ok $conf->default_project_path($path), 'set default project path'; 49 | 50 | is $conf->default_project_name, $name, 'default project name'; 51 | is $conf->default_project_path, $path->stringify, 'default project path'; 52 | 53 | done_testing; 54 | -------------------------------------------------------------------------------- /t/observer.t: -------------------------------------------------------------------------------- 1 | # 2 | # Test the Observer role 3 | # 4 | # Adapted after the 'collection_with_roles.t' test from Moose; 5 | # 6 | use 5.010; 7 | use strict; 8 | use warnings; 9 | 10 | use Test::More; 11 | 12 | { 13 | package Counter; 14 | 15 | use Moo; 16 | use Types::Standard qw(Int); 17 | 18 | with 'App::Sqitch::GUI::Roles::Observable'; 19 | 20 | has count => ( 21 | is => 'rw', 22 | isa => Int, 23 | default => 0, 24 | ); 25 | 26 | sub inc_counter { 27 | my $self = shift; 28 | $self->count( $self->count + 1); 29 | return; 30 | } 31 | 32 | sub dec_counter { 33 | my $self = shift; 34 | $self->count( $self->count - 1); 35 | return; 36 | } 37 | 38 | after qw(inc_counter dec_counter) => sub { 39 | my ($self) = @_; 40 | $self->notify(); 41 | }; 42 | } 43 | 44 | { 45 | package Display; 46 | 47 | use Moo; 48 | use Test::More; 49 | 50 | with 'App::Sqitch::GUI::Roles::Observer'; 51 | 52 | sub update { 53 | my ( $self, $subject ) = @_; 54 | like $subject->count, qr{^-?\d+$}, 55 | 'Observed number ' . $subject->count; 56 | } 57 | } 58 | 59 | package main; 60 | 61 | my $count = Counter->new(); 62 | 63 | ok( $count->can('add_observer'), 'add_observer method added' ); 64 | 65 | ok( $count->can('count_observers'), 'count_observers method added' ); 66 | 67 | ok( $count->can('inc_counter'), 'inc_counter method added' ); 68 | 69 | ok( $count->can('dec_counter'), 'dec_counter method added' ); 70 | 71 | $count->add_observer( Display->new() ); 72 | 73 | is( $count->count_observers, 1, 'Only one observer' ); 74 | 75 | is( $count->count, 0, 'Default to zero' ); 76 | 77 | $count->inc_counter; 78 | 79 | is( $count->count, 1, 'Increment to one ' ); 80 | 81 | $count->inc_counter for ( 1 .. 6 ); 82 | 83 | is( $count->count, 7, 'Increment up to seven' ); 84 | 85 | $count->dec_counter; 86 | 87 | is( $count->count, 6, 'Decrement to 6' ); 88 | 89 | $count->dec_counter for ( 1 .. 5 ); 90 | 91 | is( $count->count, 1, 'Decrement to 1' ); 92 | 93 | $count->dec_counter for ( 1 .. 2 ); 94 | 95 | is( $count->count, -1, 'Negative numbers' ); 96 | 97 | $count->inc_counter; 98 | 99 | is( $count->count, 0, 'Back to zero' ); 100 | 101 | done_testing; 102 | -------------------------------------------------------------------------------- /t/planitem.t: -------------------------------------------------------------------------------- 1 | # 2 | # Testing App::Sqitch::GUI::Model::PlanItem 3 | # 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use Test::More; 9 | use lib qw( lib ../lib ); 10 | 11 | use Path::Class; 12 | use App::Sqitch::GUI::Model::PlanItem; 13 | 14 | ok my $plan = App::Sqitch::GUI::Model::PlanItem->new, 15 | 'new plan item'; 16 | 17 | is $plan->item, undef, 'plan item'; 18 | is $plan->name, undef, 'plan name'; 19 | is $plan->change_id, undef, 'change id'; 20 | 21 | ok !$plan->item(0), 'set plan item'; 22 | ok $plan->name('appschema'), 'set plan name'; 23 | 24 | is $plan->item, 0, 'plan item'; 25 | is $plan->name, 'appschema', 'plan name'; 26 | is $plan->change_id, undef, 'change id'; 27 | 28 | ok $plan->item(1), 'set plan item'; 29 | ok $plan->name('users'), 'set plan name'; 30 | ok $plan->change_id('8d77c5f588b60bc0f2efcda6369df5cb0177521d'), 'set change id'; 31 | 32 | is $plan->item, 1, 'plan item'; 33 | is $plan->name, 'users', 'plan name'; 34 | is $plan->change_id, '8d77c5f588b60bc0f2efcda6369df5cb0177521d', 'change id'; 35 | 36 | done_testing; 37 | -------------------------------------------------------------------------------- /t/status.t: -------------------------------------------------------------------------------- 1 | use 5.010; 2 | use strict; 3 | use warnings; 4 | use Test::Most; 5 | 6 | use App::Sqitch::GUI; 7 | use App::Sqitch::GUI::Refresh; 8 | use App::Sqitch::GUI::View::Dialog::Refresh; 9 | 10 | ok my $app = App::Sqitch::GUI->new, 'new GUI'; 11 | 12 | ok my $ctrl = $app->controller, 'get the controller'; 13 | isa_ok $ctrl, 'App::Sqitch::GUI::Controller', 'controller'; 14 | 15 | ok my $view = $ctrl->view, 'get the view'; 16 | isa_ok $view, 'App::Sqitch::GUI::View', 'view'; 17 | 18 | my $dialog = App::Sqitch::GUI::View::Dialog::Projects->new( 19 | app => $ctrl->app, 20 | ancestor => $ctrl, 21 | parent => undef, # undef for dialogs 22 | ); 23 | 24 | subtest 'GUI Status' => sub { 25 | 26 | use_ok 'App::Sqitch::GUI::Status'; 27 | 28 | can_ok 'App::Sqitch::GUI::Status', qw( 29 | set_state 30 | get_state 31 | is_state 32 | ); 33 | 34 | ok my $status = App::Sqitch::GUI::Status->new, 'new GUI status instance'; 35 | isa_ok $status, 'App::Sqitch::GUI::Status', 'GUI status'; 36 | 37 | ok my $gui_ref = App::Sqitch::GUI::Refresh->new( view => $view ), 38 | 'new GUI refresh instance'; 39 | isa_ok $gui_ref, 'App::Sqitch::GUI::Refresh', 'GUI refresh'; 40 | 41 | ok $status->add_observer( $gui_ref ), 'add observer'; 42 | 43 | for my $state (qw(edit idle init load)) { 44 | ok $status->set_state($state), "set state $state"; 45 | is $status->get_state, $state, "get state ($state)"; 46 | ok $status->is_state($state), "is state $state"; 47 | } 48 | 49 | throws_ok { $status->set_state('unknown') } 50 | qr/\QValue "unknown" did not pass type constraint "Enum[edit,idle,init,load]"/, 51 | qq{'unknown' should not be a valid mode}; 52 | 53 | }; 54 | 55 | subtest 'GUI Dialog Status' => sub { 56 | 57 | use_ok 'App::Sqitch::GUI::View::Dialog::Status'; 58 | 59 | can_ok 'App::Sqitch::GUI::View::Dialog::Status', qw( 60 | set_state 61 | get_state 62 | is_state 63 | ); 64 | 65 | ok my $status = App::Sqitch::GUI::View::Dialog::Status->new, 'new GUI status instance'; 66 | isa_ok $status, 'App::Sqitch::GUI::View::Dialog::Status', 'GUI dialog status'; 67 | 68 | ok my $gui_ref = App::Sqitch::GUI::View::Dialog::Refresh->new( dialog => $dialog ), 69 | 'new GUI refresh instance'; 70 | isa_ok $gui_ref, 'App::Sqitch::GUI::View::Dialog::Refresh', 'GUI dialog refresh'; 71 | 72 | ok $status->add_observer( $gui_ref ), 'add observer'; 73 | 74 | for my $state (qw(add edit idle init sele)) { 75 | ok $status->set_state($state), "set state $state"; 76 | is $status->get_state, $state, "get state ($state)"; 77 | ok $status->is_state($state), "is state $state"; 78 | } 79 | 80 | throws_ok { $status->set_state('unknown') } 81 | qr/\QValue "unknown" did not pass type constraint "Enum[add,edit,idle,init,new,sele]"/, 82 | qq{'unknown' should not be a valid mode}; 83 | 84 | }; 85 | 86 | 87 | done_testing; 88 | -------------------------------------------------------------------------------- /t/target.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Test::Exception; 5 | use Path::Class qw(dir file); 6 | 7 | use App::Sqitch::GUI::Config; 8 | use App::Sqitch::GUI::Sqitch; 9 | 10 | my $CLASS; 11 | BEGIN { 12 | $CLASS = 'App::Sqitch::GUI::Target'; 13 | use_ok $CLASS or die; 14 | } 15 | 16 | ############################################################################## 17 | # Load a target and test the basics. 18 | 19 | 20 | $ENV{HOME} = dir('t', 'home')->stringify; # set HOME for testing 21 | 22 | # protect against user's environment variables (from Sqitch) 23 | delete @ENV{qw( SQITCH_CONFIG SQITCH_USER_CONFIG SQITCH_SYSTEM_CONFIG )}; 24 | 25 | ok my $config = App::Sqitch::GUI::Config->new, 'new config instance'; 26 | ok $config->current_project_path( dir( 't', 'home', 'flipr' ) ), 27 | 'set current path'; 28 | ok my $sqitch = App::Sqitch::GUI::Sqitch->new( 29 | options => { engine => 'sqlite' }, 30 | config => $config, 31 | ), 'Load a sqitch sqitch object'; 32 | is $sqitch->config->current_project_path, dir( 't', 'home', 'flipr' ), 33 | 'get current path'; 34 | 35 | isa_ok my $target = $CLASS->new( 36 | sqitch => $sqitch, 37 | top_dir => $config->current_project_path, 38 | ), $CLASS; 39 | 40 | can_ok $target, qw( 41 | new 42 | name 43 | target 44 | uri 45 | sqitch 46 | engine 47 | registry 48 | client 49 | plan_file 50 | plan 51 | top_dir 52 | deploy_dir 53 | revert_dir 54 | verify_dir 55 | extension 56 | ); 57 | 58 | # Look at default values. 59 | is $target->name, 'db:sqlite:', 'Name should be "db:sqlite:"'; 60 | is $target->target, $target->name, 'Target should be alias for name'; 61 | is $target->uri, URI::db->new('db:sqlite:'), 'URI should be "db:sqlite:"'; 62 | is $target->sqitch, $sqitch, 'Sqitch should be as passed'; 63 | is $target->engine_key, 'sqlite', 'Engine key should be "sqlite"'; 64 | isa_ok $target->engine, 'App::Sqitch::Engine::sqlite', 'Engine'; 65 | is $target->registry, $target->engine->default_registry, 66 | 'Should have default registry'; 67 | my $client = $target->engine->default_client; 68 | $client .= '.exe' if $^O eq 'MSWin32' && $client !~ /[.](?:exe|bat)$/; 69 | is $target->client, $client, 'Should have default client'; 70 | 71 | is $target->top_dir, dir( 't', 'home', 'flipr' ), 'Should have custom top_dir'; 72 | # throws_ok { $target->top_dir, dir( 't', 'home', 'flipr' ) } qr/^Missing required arguments:/, 73 | # 'Should get error for missing params'; 74 | 75 | is $target->deploy_dir, $target->top_dir->subdir('deploy'), 76 | 'Should have default deploy_dir'; 77 | is $target->revert_dir, $target->top_dir->subdir('revert'), 78 | 'Should have default revert_dir'; 79 | is $target->verify_dir, $target->top_dir->subdir('verify'), 80 | 'Should have default verify_dir'; 81 | is $target->extension, 'sql', 'Should have default extension'; 82 | is $target->plan_file, $target->top_dir->file('sqitch.plan')->cleanup, 83 | 'Should have default plan file'; 84 | isa_ok $target->plan, 'App::Sqitch::Plan', 'Should get plan'; 85 | is $target->plan->file, $target->plan_file, 86 | 'Plan file should be copied from Target'; 87 | my $uri = $target->uri; 88 | is $target->dsn, $uri->dbi_dsn, 'DSN should be from URI'; 89 | is $target->username, $uri->user, 'Username should be from URI'; 90 | is $target->password, $uri->password, 'Password should be from URI'; 91 | 92 | do { 93 | isa_ok my $target = $CLASS->new( 94 | sqitch => $sqitch, 95 | top_dir => $config->current_project_path, 96 | ), $CLASS; 97 | local $ENV{SQITCH_PASSWORD} = 'S3cre7s'; 98 | is $target->password, $ENV{SQITCH_PASSWORD}, 99 | 'Password should be from environment variable'; 100 | }; 101 | 102 | done_testing; 103 | -------------------------------------------------------------------------------- /t/timezone.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | 5 | use DateTime::TimeZone::Local; 6 | 7 | ok my $tz = DateTime::TimeZone::Local->TimeZone()->name; 8 | diag "TZ: $tz"; 9 | isnt $tz, '', 'have a time zone'; 10 | 11 | done_testing; 12 | -------------------------------------------------------------------------------- /t/wx-listctrl.t: -------------------------------------------------------------------------------- 1 | # 2 | # Inspired from the tests of the Wx-Scintilla module, 3 | # Copyright (C) 2011 Ahmad M. Zawawi, 4 | # the MyTimer package is copied verbatim. 5 | # 6 | use strict; 7 | use warnings; 8 | 9 | use Test::More; 10 | 11 | BEGIN { 12 | unless ( $ENV{DISPLAY} or $^O eq 'MSWin32' ) { 13 | plan skip_all => 'Needs DISPLAY'; 14 | exit 0; 15 | } 16 | eval { require Wx; }; 17 | if ($@) { 18 | plan( skip_all => 'wxPerl is required for this test' ); 19 | } 20 | } 21 | 22 | package MyTimer; 23 | 24 | use Wx qw(:everything); 25 | use Wx::Event; 26 | 27 | use base qw(Wx::Timer); 28 | 29 | sub Notify { 30 | my $self = shift; 31 | my $frame = Wx::wxTheApp()->GetTopWindow; 32 | $frame->Destroy; 33 | main::ok( 1, "ListCtrl instance destroyed" ); 34 | } 35 | 36 | package TestApp; 37 | 38 | use strict; 39 | use warnings; 40 | 41 | use Wx qw(:everything); 42 | use Wx::Event; 43 | use base 'Wx::App'; 44 | 45 | use App::Sqitch::GUI::Model::ListDataTable; 46 | use App::Sqitch::GUI::Wx::Listctrl; 47 | 48 | sub list_meta_data { 49 | return [ 50 | { field => 'recno', 51 | label => '#', 52 | align => 'center', 53 | width => 25, 54 | type => 'int', 55 | }, 56 | { field => 'name', 57 | label => 'Name', 58 | align => 'left', 59 | width => 100, 60 | type => 'str', 61 | }, 62 | { field => 'path', 63 | label => 'Path', 64 | align => 'left', 65 | width => 266, 66 | type => 'bool', 67 | }, 68 | ]; 69 | } 70 | 71 | sub OnInit { 72 | my $self = shift; 73 | 74 | my $frame = Wx::Frame->new( undef, -1, 'Test!', ); 75 | 76 | my $list_data = App::Sqitch::GUI::Model::ListDataTable->new; 77 | 78 | my $list_ctrl = App::Sqitch::GUI::Wx::Listctrl->new( 79 | app => undef, 80 | parent => $frame, 81 | list_data => $list_data, 82 | meta_data => list_meta_data(), 83 | ); 84 | 85 | main::ok( $list_ctrl, 'Listctrl instance created' ); 86 | main::is( $list_ctrl->set_selection('last'), -1, 'select last item (-1)' ); 87 | 88 | # Fill the table 89 | foreach my $iter ( 1..5 ) { 90 | main::ok( $list_data->add_row( $iter, "Name $iter", "Path $iter" ), 91 | "add row: $iter" ); 92 | $list_ctrl->RefreshList; 93 | } 94 | # print "Data:\n", $list_data->get_data, "\n"; 95 | 96 | # Delete even items 97 | main::ok $list_data->remove_row(1), "remove row 2"; 98 | main::ok $list_data->remove_row(2), "remove row 4"; 99 | $list_ctrl->RefreshList; 100 | # print "Data:\n", $list_data->get_data, "\n"; 101 | main::is $list_data->get_item_count, 3, 'item count: 3 values'; 102 | 103 | main::is( $list_ctrl->set_selection('first'), 0, 'select first item (0)' ); 104 | main::is( $list_ctrl->set_selection('last'), 2, 'select last item (2)' ); 105 | 106 | # Uncomment this to observe the test 107 | #$frame->Show(1) if $ENV{DISPLAY}; 108 | 109 | MyTimer->new->Start( 500, 1 ); 110 | 111 | return 1; 112 | } 113 | 114 | # Create the application object, and pass control to it. 115 | package main; 116 | 117 | my $app = TestApp->new; 118 | $app->MainLoop; 119 | 120 | done_testing; 121 | -------------------------------------------------------------------------------- /t/wx-toolbar.t: -------------------------------------------------------------------------------- 1 | # 2 | # Inspired from the tests of the Wx-Scintilla module, 3 | # Copyright (C) 2011 Ahmad M. Zawawi, 4 | # the MyTimer package is copied verbatim. 5 | # 6 | use strict; 7 | use warnings; 8 | 9 | use Test::More; 10 | 11 | BEGIN { 12 | unless ( $ENV{DISPLAY} or $^O eq 'MSWin32' ) { 13 | plan skip_all => 'Needs DISPLAY'; 14 | exit 0; 15 | } 16 | eval { require Wx; }; 17 | if ($@) { 18 | plan( skip_all => 'wxPerl is required for this test' ); 19 | } 20 | } 21 | 22 | package MyTimer; 23 | 24 | use Wx qw(:everything); 25 | use Wx::Event; 26 | 27 | use base qw(Wx::Timer); 28 | 29 | sub Notify { 30 | my $self = shift; 31 | my $frame = Wx::wxTheApp()->GetTopWindow; 32 | $frame->Destroy; 33 | main::ok( 1, "Toolbar instance destroyed" ); 34 | } 35 | 36 | package TestApp; 37 | 38 | use strict; 39 | use warnings; 40 | 41 | use Path::Class; 42 | use Wx qw(:everything); 43 | use Wx::Event; 44 | use base 'Wx::App'; 45 | 46 | use App::Sqitch::GUI::Config::Toolbar; 47 | use App::Sqitch::GUI::Wx::Toolbar; 48 | 49 | sub OnInit { 50 | my $self = shift; 51 | 52 | my $frame = Wx::Frame->new( undef, -1, 'Test!', ); 53 | 54 | # Tool Bar 55 | my $conf = App::Sqitch::GUI::Config::Toolbar->new; 56 | main::ok( $conf, 'new Toolbar config instance' ); 57 | 58 | main::ok my @toolbars = $conf->all_buttons, 'all buttons attributes'; 59 | 60 | main::ok my $tb = App::Sqitch::GUI::Wx::Toolbar->new( 61 | app => undef, 62 | ancestor => $self, 63 | parent => $frame, 64 | icon_path => dir('share', 'etc', 'icons'), 65 | ), 'new Toolbar instance'; 66 | 67 | foreach my $name (@toolbars) { 68 | my $attribs = $conf->get_tool($name); 69 | $tb->make_toolbar_button( $name, $attribs ); 70 | } 71 | $tb->set_initial_mode( \@toolbars ); 72 | 73 | $frame->SetToolBar($tb); 74 | 75 | 76 | 77 | # Uncomment this to observe the test 78 | # $frame->Show(1) if $ENV{DISPLAY}; 79 | 80 | MyTimer->new->Start( 500, 1 ); 81 | 82 | return 1; 83 | } 84 | 85 | # Create the application object, and pass control to it. 86 | package main; 87 | 88 | my $app = TestApp->new; 89 | $app->MainLoop; 90 | 91 | done_testing; 92 | -------------------------------------------------------------------------------- /xt/manifest.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::More; 5 | 6 | unless ( $ENV{RELEASE_TESTING} ) { 7 | plan( skip_all => "Author tests not required for installation" ); 8 | } 9 | 10 | eval "use Test::CheckManifest 0.9"; 11 | plan skip_all => "Test::CheckManifest 0.9 required" if $@; 12 | ok_manifest(); 13 | -------------------------------------------------------------------------------- /xt/pod-coverage.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::More; 5 | 6 | unless ( $ENV{RELEASE_TESTING} ) { 7 | plan( skip_all => "Author tests not required for installation" ); 8 | } 9 | 10 | # Ensure a recent version of Test::Pod::Coverage 11 | my $min_tpc = 1.08; 12 | eval "use Test::Pod::Coverage $min_tpc"; 13 | plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage" 14 | if $@; 15 | 16 | # Test::Pod::Coverage doesn't require a minimum Pod::Coverage version, 17 | # but older versions don't recognize some common documentation styles 18 | my $min_pc = 0.18; 19 | eval "use Pod::Coverage $min_pc"; 20 | plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage" 21 | if $@; 22 | 23 | all_pod_coverage_ok(); 24 | -------------------------------------------------------------------------------- /xt/pod.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::More; 5 | 6 | unless ( $ENV{RELEASE_TESTING} ) { 7 | plan( skip_all => "Author tests not required for installation" ); 8 | } 9 | 10 | # Ensure a recent version of Test::Pod 11 | my $min_tp = 1.22; 12 | eval "use Test::Pod $min_tp"; 13 | plan skip_all => "Test::Pod $min_tp required for testing POD" if $@; 14 | 15 | all_pod_files_ok(); 16 | --------------------------------------------------------------------------------