[IMP] rewrite of search view/view manager
authorGéry Debongnie <ged@odoo.com>
Tue, 7 Oct 2014 14:27:43 +0000 (16:27 +0200)
committerGéry Debongnie <ged@odoo.com>
Tue, 21 Oct 2014 07:33:50 +0000 (09:33 +0200)
Second part of a large refactoring.  Lots of cleanup, simpler code organization, and it should be faster.

addons/web/static/src/css/base.css
addons/web/static/src/css/base.sass
addons/web/static/src/js/search.js
addons/web/static/src/js/views.js
addons/web/static/src/xml/base.xml

index 23f5782..61938b8 100644 (file)
   filter: alpha(opacity=50);
   opacity: 0.5;
 }
-.openerp .oe_sidebar {
-  white-space: nowrap;
-}
-.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment {
-  height: 20px;
-  cursor: pointer;
-  padding-left: 6px;
-  margin-top: 6px;
-}
-.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment span {
-  font-weight: bold;
-}
-.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment .oe_hidden_input_file {
-  width: 200px;
-}
-.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment:hover {
-  background-color: #efeff8;
-  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
-  background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6);
-  background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6);
-  background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6);
-  background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
-  background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
-  -moz-box-shadow: none;
-  -webkit-box-shadow: none;
-  box-shadow: none;
-}
-.openerp .oe_sidebar .oe_dropdown_menu li .oe_sidebar_delete_item {
-  position: absolute;
-  top: 4px;
-  right: 4px;
-  display: none;
-  width: 12px;
-  height: 12px;
-  padding: 1px;
-  color: #8786b7;
-  line-height: 8px;
-  text-align: center;
-  font-weight: bold;
-  text-shadow: 0 1px 1px white;
-}
-.openerp .oe_sidebar .oe_dropdown_menu li .oe_sidebar_delete_item:hover {
-  text-decoration: none;
-  color: white;
-  background: #8786b7;
-  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
-  -moz-border-radius: 2px;
-  -webkit-border-radius: 2px;
-  border-radius: 2px;
-}
-.openerp .oe_sidebar .oe_dropdown_menu li:hover .oe_sidebar_delete_item {
-  display: inline-block;
-}
 .openerp .oe_loading {
   display: none;
   z-index: 1001;
 .openerp .oe_application {
   width: 100%;
   height: 100%;
-}
-.openerp .oe_application .oe-view-manager-body a {
-  color: #7C7BAD;
-}
-.openerp .oe_application .oe-view-manager-body .oe-view-manager-view-kanban:not(:empty) {
-  height: 100%;
+  overflow: hidden;
 }
 .openerp .oe_application > div {
   position: relative;
   height: 100%;
 }
-.openerp .oe_application > div > .oe_view_manager > .oe_view_manager_wrapper {
+.openerp .oe_application .oe-view-manager .oe-view-manager-debug {
+  margin-right: 5px;
+}
+.openerp .oe_application .oe-view-manager .oe-view-manager-content {
   display: table-row;
   height: 100%;
 }
-.openerp .oe_application > div > .oe_view_manager > .oe_view_manager_wrapper > div {
+.openerp .oe_application .oe-view-manager-body {
   position: relative;
   height: 100%;
 }
-.openerp .oe_application > div > .oe_view_manager > .oe_view_manager_wrapper > div > .oe_view_manager_body {
+.openerp .oe_application .oe-view-manager-body a {
+  color: #7C7BAD;
+}
+.openerp .oe_application .oe-view-manager-body .oe-view-manager-view-kanban:not(:empty) {
+  height: 100%;
+}
+.openerp .oe_application .oe-view-manager-body > div {
   position: absolute;
-  position: static\9;
   top: 0;
   bottom: 0;
   left: 0;
   right: 0;
   overflow: auto;
 }
-.openerp .oe_application .oe_breadcrumb_item:not(:last-child) {
-  max-width: 7em;
-  white-space: nowrap;
-  text-overflow: ellipsis;
+.openerp .oe_application .oe_form_field_one2many .oe-view-manager-views {
+  position: initial;
 }
-.openerp .oe_application .oe_breadcrumb_title > * {
-  display: inline-block;
-  overflow: hidden;
-  font-weight: bold;
+.openerp .oe-view-manager {
+  display: table;
+  width: 100%;
+  height: 100%;
 }
 .openerp .oe-view-manager-header {
-  height: 90px;
-  overflow: visible;
-  border-bottom: 1px solid black;
-  display: none;
+  display: table-row;
+  background-color: #f0eeee;
+  border-bottom: 1px solid #afafb6;
 }
 .openerp .oe-view-manager-header .oe-right-toolbar {
   float: right;
   display: inline-block;
 }
 .openerp .oe-view-manager-header .row {
-  height: 45px;
+  margin: 0;
+}
+.openerp .oe-view-manager-header .row:first-child {
+  padding-top: 3px;
+  padding-bottom: 3px;
+}
+.openerp .oe-view-manager-header .row:last-child {
+  padding-bottom: 10px;
+}
+.openerp .oe-view-manager-header .oe_tag {
+  -moz-border-radius: 0px;
+  -webkit-border-radius: 0px;
+  border-radius: 0px;
 }
 .openerp .oe-view-manager-header .oe-view-title {
-  float: left;
   font-size: 18px;
-  font-weight: bold;
-  margin: 0;
-}
-.openerp .oe-view-manager-header .oe-header-title {
   padding-left: 0;
+  margin: 0;
+  background-color: #f0eeee;
 }
 .openerp .oe-view-manager-header .oe-view-manager-search-view {
-  float: right;
   padding-top: 5px;
 }
 .openerp .oe-view-manager-header .oe-view-manager-switch .oe-vm-switch-kanban:before {
   content: "";
 }
 .openerp .oe-view-manager-header .oe-view-manager-switch .oe-vm-switch-list:before {
-  content: "";
+  content: "";
 }
 .openerp .oe-view-manager-header .oe-view-manager-switch .oe-vm-switch-form:before {
   content: "";
 .openerp .oe-view-manager-header .oe-view-manager-switch .oe-vm-switch-gantt:before {
   content: "";
 }
+.openerp .oe-view-manager-header .oe-view-manager-switch .oe-vm-switch-pivot:before {
+  content: "";
+}
 .openerp .oe-view-manager-header .oe-view-manager-buttons {
-  float: left;
+  display: inline-block;
 }
 .openerp .oe-view-manager-header .oe-view-manager-buttons > div {
   display: none;
 }
-.openerp .oe-view-manager-header .oe_pager_value {
+.openerp .oe-view-manager-header .oe-view-manager-sidebar {
   display: inline-block;
+  float: right;
+}
+.openerp .oe-view-manager-header .oe_form_buttons {
+  padding: 0;
+}
+.openerp .oe-view-manager-header .oe-pager-buttons {
+  min-height: 30px;
+}
+.openerp .oe_view_manager_inline > .oe-view-manager-header, .openerp .oe_view_manager_inlineview > .oe-view-manager-header {
+  display: none;
 }
 .openerp .oe_application > div > div > .oe-view-manager-body {
   position: absolute;
   right: 0;
   overflow: auto;
 }
-.openerp .oe_view_manager_inline, .openerp .oe_view_manager_inlineview {
-  height: 100%;
-}
-.openerp .oe_view_manager_inline > .oe_view_manager_header, .openerp .oe_view_manager_inlineview > .oe_view_manager_header {
-  display: none;
-}
 .openerp .oe_popup_form {
   display: table;
 }
 .openerp .oe_popup_list_pager {
   float: right;
 }
+.openerp .oe_popup_search {
+  width: 50%;
+  float: right;
+}
 .openerp .oe_searchview {
   cursor: text;
   position: relative;
-  padding: 1px 0;
-  min-width: 400px;
-  width: 400px;
-  -moz-border-radius: 13px;
-  -webkit-border-radius: 13px;
-  border-radius: 13px;
-}
-.openerp .oe_searchview input, .openerp .oe_searchview textarea {
-  padding: 3px;
-  height: 14px;
-  font-size: 12px;
-  line-height: 18px;
-}
-.openerp .oe_searchview input:not([type]), .openerp .oe_searchview input[type="text"], .openerp .oe_searchview input[type="number"] {
-  width: 156px;
-  height: 22px;
-}
-.openerp .oe_searchview input[type="checkbox"] {
-  margin: 3px 3px 3px 4px;
-}
-.openerp .oe_searchview select {
-  margin: 2px 4px 2px 0;
-}
-.openerp .oe_searchview.oe_focused {
-  border-color: #a6a6fe;
-  -moz-box-shadow: 0 1px 2px #a6a6fe inset;
-  -webkit-box-shadow: 0 1px 2px #a6a6fe inset;
-  box-shadow: 0 1px 2px #a6a6fe inset;
-}
-.openerp .oe_searchview .oe_searchview_clear {
-  cursor: pointer;
-  position: absolute;
-  top: 3px;
-  right: 18px;
-  width: 15px;
-  height: 24px;
+  display: none;
+  height: auto;
+  padding-top: 1px;
+  padding-bottom: 1px;
 }
 .openerp .oe_searchview .oe_searchview_unfold_drawer {
   position: absolute;
-  top: 0;
-  right: 0;
-  color: #ccc;
+  top: 8px;
+  left: 5px;
   cursor: pointer;
 }
-.openerp .oe_searchview .oe_searchview_unfold_drawer:hover {
-  color: #999;
-}
-.openerp .oe_searchview .oe_searchview_unfold_drawer:before {
-  position: absolute;
-  top: 7px;
-  right: 18px;
-  width: 0;
-}
 .openerp .oe_searchview .oe_searchview_search {
   cursor: pointer;
   position: absolute;
-  left: 6px;
+  right: 6px;
   top: 8px;
 }
 .openerp .oe_searchview .oe_searchview_facets {
   min-height: 22px;
-  margin: 2px 35px 0 20px;
+  margin: 2px 20px 0 10px;
 }
 .openerp .oe_searchview .oe_searchview_facets * {
   vertical-align: top;
   font-size: 100%;
 }
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
-  height: 20px;
   margin: 1px 0;
   font-size: 11px;
 }
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet:focus {
   outline: none;
 }
+.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .label {
+  -moz-border-radius: 0px;
+  -webkit-border-radius: 0px;
+  border-radius: 0px;
+}
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_input {
   padding: 0 0 0 6px;
   font-size: 12px;
   height: 16px;
-  margin-top: 3px;
 }
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_input:focus {
   outline: none;
   padding: 0;
   -webkit-font-smoothing: auto;
 }
-.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet span {
-  height: 18px;
-}
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet:focus {
   border-color: #a6a6fe;
   -moz-box-shadow: 0 0 3px 1px #a6a6fe;
   font-size: 16px;
 }
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value {
+  border-left: 1px solid #afafb6;
   color: #4C4C4C;
 }
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value:last-child {
   padding-right: 16px;
 }
+.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value:first-child {
+  border-left: none;
+}
 .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_remove {
   position: absolute;
   top: 3px;
 .openerp .oe_searchview .oe-autocomplete {
   display: none;
   position: absolute;
-  width: 300px;
   background-color: white;
+  width: 400px;
   border: 1px solid #afafb6;
   z-index: 666;
   margin-top: 2px;
   -moz-border-radius: 3px;
   -webkit-border-radius: 3px;
   border-radius: 3px;
-  -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-  -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
 }
 .openerp .oe_searchview .oe-autocomplete ul {
   list-style-type: none;
 .openerp .oe_searchview .oe-autocomplete ul li.oe-separator:last-child {
   display: none;
 }
-.openerp .oe_searchview_drawer_container {
-  overflow: auto;
-}
-.openerp .oe_searchview_drawer {
-  display: none;
+.openerp .oe-search-options a {
+  padding-left: 25px;
+  padding-right: 25px;
   width: 100%;
-  cursor: default;
-  display: none;
-  overflow: hidden;
-  border-bottom: 1px solid #afafb6;
-  text-align: left;
-  padding: 8px 0;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  user-select: none;
 }
-.openerp .oe_searchview_drawer .badge {
-  font-size: 12px;
-  line-height: 12px;
+.openerp .oe-search-options .oe-apply-filter {
+  margin-left: 25px;
 }
-.openerp .oe_searchview_drawer > div:first-child {
-  border: none;
-  padding-left: 0;
-}
-.openerp .oe_searchview_drawer > div:first-child li:hover:not(.badge) {
-  background-color: #f0f0fa;
+.openerp .oe-search-options .oe-add-filter-menu {
+  display: none;
 }
-.openerp .oe_searchview_drawer .col-md-5 {
+.openerp .oe-search-options .oe-add-condition {
+  width: auto;
+  margin-left: 10px;
   padding-left: 0;
+  padding-right: 10px;
 }
-.openerp .oe_searchview_drawer dl {
-  margin-bottom: 0;
-}
-.openerp .oe_searchview_drawer dt {
-  color: #7C7BAD;
-  font-size: 13px;
-  line-height: 24px;
-}
-.openerp .oe_searchview_drawer dd {
-  line-height: 24px;
-  font-size: 13px;
-  padding-top: 3px;
-}
-.openerp .oe_searchview_drawer h4, .openerp .oe_searchview_drawer h4 * {
-  margin: 0 0 0 2px;
-  padding-left: 20px;
-  cursor: pointer;
-  font-weight: normal;
-  display: inline-block;
-}
-.openerp .oe_searchview_drawer h4:hover, .openerp .oe_searchview_drawer h4 *:hover {
-  background-color: #f0f0fa;
-}
-.openerp .oe_searchview_drawer h4:before {
-  content: "▸ ";
-  color: #a3a3a3;
+.openerp .oe-search-options .selected {
+  display: block;
 }
-.openerp .oe_searchview_drawer button {
-  margin: 4px 0;
+.openerp .oe-search-options .selected a {
+  font-weight: bold;
 }
-.openerp .oe_searchview_drawer .button {
-  border: none;
-  background: transparent;
-  padding: 0 2px;
-  -moz-box-shadow: none;
-  -webkit-box-shadow: none;
-  box-shadow: none;
-  -moz-border-radius: 0;
-  -webkit-border-radius: 0;
-  border-radius: 0;
+.openerp .oe-search-options .selected a:before {
+  font-family: FontAwesome;
+  position: absolute;
+  left: 6px;
+  top: 3px;
+  content: "";
 }
-.openerp .oe_searchview_drawer .oe_searchview_section ul {
-  margin: 0 8px;
-  padding: 0;
-  list-style: none;
-  display: inline;
+.openerp .oe-search-options .oe-group-selector {
+  margin: 3px 25px;
+  min-width: calc(100% - 40px);
+  max-width: 250px;
+  width: auto;
 }
-.openerp .oe_searchview_drawer .oe_searchview_section li {
-  display: inline-block;
-  cursor: pointer;
-  position: relative;
-  margin-right: 8px;
+.openerp .oe-search-options .divider {
+  display: none;
 }
-.openerp .oe_searchview_drawer .oe_searchview_section li > span {
-  display: inline-block;
-  max-width: 250px;
-  text-overflow: ellipsis;
-  vertical-align: bottom;
-  overflow: hidden;
+.openerp .oe-search-options .filters-menu .divider {
+  display: list-item;
 }
-.openerp .oe_searchview_drawer form {
-  margin-left: 12px;
+.openerp .oe-search-options .closed-menu a:before {
+  font-family: FontAwesome;
+  position: absolute;
+  left: 12px;
+  top: 3px;
+  content: "";
 }
-.openerp .oe_searchview_drawer form p {
-  margin: 4px 0;
-  line-height: 18px;
+.openerp .oe-search-options .open-menu a:before {
+  font-family: FontAwesome;
+  position: absolute;
+  left: 9px;
+  top: 3px;
+  content: "";
 }
-.openerp .oe_searchview_drawer form button {
-  margin: 0 0 8px -3px;
+.openerp .oe-search-options .oe-select-group {
+  margin: 3px 25px;
 }
-.openerp .oe_searchview_drawer .oe_searchview_savefilter form {
+.openerp .oe-search-options .oe-add-group {
   display: none;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom {
+.openerp .oe-search-options .oe-save-name {
   display: none;
+  margin: 3px 25px;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom li {
-  cursor: pointer;
-  position: relative;
-  line-height: 14px;
-  margin-right: 0;
+.openerp .oe-search-options .oe-save-name span {
+  white-space: nowrap;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom li button {
-  position: absolute;
-  top: 0;
-  right: 5px;
+.openerp .oe-search-options .oe-save-name span input {
+  margin-left: 0;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom li a {
-  margin-left: 10px;
-  position: inherit;
-  visibility: hidden;
-  display: inline-block;
+.openerp .oe-search-options .searchview_extended_prop_field {
+  display: inline;
+  width: calc(100% - 20px);
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom li span:hover:not(.badge) {
-  background-color: #f0f0fa;
+.openerp .oe-search-options .searchview_extended_prop_op {
+  margin: 3px 0;
+  width: calc(100% - 20px);
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom li:hover a {
-  visibility: visible;
+.openerp .oe-search-options .searchview_extended_delete_prop {
+  float: right;
+  display: inline;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom label {
-  font-weight: normal;
+.openerp .oe-search-options .searchview_extended_prop_value {
+  width: calc(100% - 20px);
 }
-.openerp .oe_searchview_drawer .oe_searchview_dashboard form {
-  display: none;
-  margin-top: 2px;
+.openerp .oe-search-options .searchview_extended_prop_value > select, .openerp .oe-search-options .searchview_extended_prop_value > input {
+  width: calc(100% - 20px);
 }
-.openerp .oe_searchview_drawer .oe_searchview_advanced {
-  overflow: auto;
+.openerp .oe-search-options .searchview_extended_prop_value .oe_datepicker_main {
+  width: calc(100% - 20px);
 }
-.openerp .oe_searchview_drawer .oe_searchview_advanced form {
-  display: none;
-  margin-top: 8px;
+.openerp .oe-search-options .searchview_extended_prop_value .oe_datepicker_master {
+  width: 100%;
 }
-.openerp .oe_searchview_drawer .oe_searchview_advanced button.oe_add_condition:before {
-  content: "Z";
-  font-family: "entypoRegular" !important;
-  font-size: 24px;
-  font-weight: 300 !important;
-  margin-right: 4px;
+.openerp .oe-search-options .searchview_extended_prop_value .fa-calendar {
+  margin-left: -21px;
+  cursor: pointer;
 }
-.openerp .oe_searchview_drawer .oe_searchview_advanced ul {
-  list-style: none;
-  padding: 0;
+.openerp .oe-search-options span.fa-trash-o {
+  position: absolute;
+  top: 6px;
+  right: 10px;
+  cursor: pointer;
 }
-.openerp .oe_searchview_drawer .oe_searchview_advanced li {
+.openerp .oe-search-options .dropdown-menu > li {
   position: relative;
-  list-style: none;
-  margin: 0;
   white-space: nowrap;
 }
-.openerp .oe_searchview_drawer .oe_searchview_advanced li:first-child .searchview_extended_prop_or {
-  visibility: hidden;
-  margin-left: -14px;
-}
-.openerp .oe_searchview_drawer .oe_searchview_advanced .searchview_extended_prop_or {
-  opacity: 0.5;
-  margin-left: -14px;
-}
-.openerp .oe_searchview_drawer .oe_opened h4:before {
-  content: "▾ ";
-  position: relative;
-  top: -1px;
-}
-.openerp .oe_searchview_drawer .oe_opened form {
-  display: block;
+.openerp .oe-search-options .dropdown-menu li.oe-filter-condition {
+  white-space: normal;
+  padding-left: 25px;
+  padding-right: 10px;
+  margin-bottom: 5px;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom_delete, .openerp .oe_searchview_drawer .searchview_extended_delete_prop {
+.openerp .oe-search-options a {
   display: inline-block;
-  width: 12px;
-  height: 12px;
-  line-height: 12px;
-  padding: 1px;
-  color: #8786b7;
-  line-height: 8px;
-  text-align: center;
-  font-weight: bold;
-  text-shadow: 0 1px 1px white;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom_delete:hover, .openerp .oe_searchview_drawer .searchview_extended_delete_prop:hover {
-  text-decoration: none;
-  color: white;
-  background: #8786b7;
-  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
-  -moz-border-radius: 2px;
-  -webkit-border-radius: 2px;
-  border-radius: 2px;
-}
-.openerp .oe_searchview_drawer .oe_searchview_custom_delete {
-  display: none;
-  position: absolute;
-  bottom: 1px;
-  right: 4px;
+.openerp input.oe-save-name {
+  width: auto;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom_private:hover .oe_searchview_custom_delete, .openerp .oe_searchview_drawer .oe_searchview_custom_public:hover .oe_searchview_custom_delete {
+.openerp .oe_pager_value {
   display: inline-block;
 }
-.openerp .oe_searchview_drawer .oe_searchview_custom_public:after {
-  content: ",";
-  font-family: "entypoRegular" !important;
-  font-size: 22px;
-  font-weight: 300 !important;
-  margin: 0 0 0 4px;
-  padding: 0;
-}
 .openerp .oe_view_nocontent {
   padding: 15px;
   margin-top: 0;
 .openerp .oe_form .oe_datepicker_root {
   display: inline-block;
 }
+.openerp .oe_form .oe_datepicker_root .datepickerbutton {
+  margin-left: 5px;
+  cursor: pointer;
+}
 .openerp .oe_form .oe_form_required input:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required select:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required textarea:not([disabled]):not([readonly]) {
   background-color: #D2D2FF !important;
 }
 }
 .openerp .oe_debug_view {
   float: left;
+  margin-top: 5px;
+  width: auto;
 }
 .openerp .oe_debug_view_log {
   font-size: 95%;
   font-size: 11px;
   background-color: #7c7bad;
 }
-.openerp button, .openerp body {
-  line-height: normal;
-}
 .openerp h1, .openerp h2 {
   font-weight: bold;
 }
 }
 
 @-moz-document url-prefix() {
-  .openerp .oe_searchview .oe_searchview_search {
-    top: -1px;
-  }
   .openerp .oe_form_field_many2one .oe_m2o_cm_button {
     line-height: 18px;
   }
index e39f04f..845c005 100644 (file)
@@ -653,44 +653,6 @@ $sheet-padding: 16px
         border-right: 4px solid transparent
         border-top: 4px solid#404040
         @include opacity(0.5)
-    .oe_sidebar
-        white-space: nowrap
-        .oe_dropdown_menu
-            .oe_sidebar_add_attachment
-                height: 20px
-                cursor: pointer
-                padding-left: 6px
-                margin-top: 6px
-                span
-                    font-weight: bold
-                .oe_hidden_input_file
-                    width: 200px
-                &:hover
-                    @include vertical-gradient(#f0f0fa, #eeeef6)
-                    @include box-shadow(none)
-            li
-                .oe_sidebar_delete_item
-                    position: absolute
-                    top: 4px
-                    right: 4px
-                    display: none
-                    width: 12px
-                    height: 12px
-                    padding: 1px
-                    color: #8786b7
-                    line-height: 8px
-                    text-align: center
-                    font-weight: bold
-                    text-shadow: 0 1px 1px white
-                    &:hover
-                        text-decoration: none
-                        color: white
-                        background: #8786b7
-                        text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
-                        @include radius(2px)
-                &:hover
-                    .oe_sidebar_delete_item
-                        display: inline-block
     // }}}
     // Loading {{{
     .oe_loading
@@ -899,67 +861,70 @@ $sheet-padding: 16px
     .oe_application
         width: 100%
         height: 100%
+        overflow: hidden
+        > div
+            position: relative
+            height: 100%
+        .oe-view-manager
+            .oe-view-manager-debug
+                margin-right: 5px
+            .oe-view-manager-content
+                display: table-row
+                height: 100%
+
         .oe-view-manager-body
+            position: relative
+            height: 100%
             a
                 color: $link-color
             .oe-view-manager-view-kanban:not(:empty)
                 height: 100%
-        > div
-            position: relative
-            height: 100%
-            > .oe_view_manager
-                > .oe_view_manager_wrapper
-                    display: table-row
-                    height: 100%
-                    > div
-                        position: relative
-                        height: 100%
-                        > .oe_view_manager_body
-                            position: absolute
-                            position: static\9
-                            top: 0
-                            bottom: 0
-                            left: 0
-                            right: 0
-                            overflow: auto
-
-
-        .oe_breadcrumb_item:not(:last-child)
-            max-width: 7em
-            white-space: nowrap
-            text-overflow: ellipsis
-        .oe_breadcrumb_title > *
-            display: inline-block
-            overflow: hidden
-            font-weight: bold
+            > div
+                position: absolute
+                top: 0
+                bottom: 0
+                left: 0
+                right: 0
+                overflow: auto
+        .oe_form_field_one2many
+            .oe-view-manager-views
+                position: initial
+
     // }}}
     // ViewManager common {{{
+    .oe-view-manager
+        display: table
+        width: 100%
+        height: 100%
     .oe-view-manager-header
-        height: 90px
-        overflow: visible
-        border-bottom: 1px solid black
-        display: none
+        display: table-row
+        background-color: rgb(240, 238, 238)
+        border-bottom: 1px solid #afafb6
         .oe-right-toolbar
             float: right
             > div
                 display: inline-block
         .row
-            height: 45px
+            margin: 0
+        .row:first-child
+            padding-top: 3px
+            padding-bottom: 3px
+        .row:last-child
+            padding-bottom: 10px
+        .oe_tag
+            @include radius(0px)
         .oe-view-title
-            float: left
             font-size: 18px
-            font-weight: bold
-            margin: 0
-        .oe-header-title
             padding-left: 0
+            margin: 0
+            background-color: rgb(240, 238, 238)
         .oe-view-manager-search-view
-            float: right
             padding-top: 5px
         .oe-view-manager-switch
             .oe-vm-switch-kanban:before
                 content: "\f009"
             .oe-vm-switch-list:before
-                content: "\f0c9"
+                content: ""
             .oe-vm-switch-form:before
                 content: "\f044"
             .oe-vm-switch-graph:before
@@ -968,12 +933,22 @@ $sheet-padding: 16px
                 content: "\f073"
             .oe-vm-switch-gantt:before
                 content: "\f0ae"
+            .oe-vm-switch-pivot:before
+                content: "\f0ce"
         .oe-view-manager-buttons
-            float: left
+            display: inline-block
             > div
                 display: none
-        .oe_pager_value
-            display: inline-block            
+        .oe-view-manager-sidebar
+            display: inline-block
+            float: right
+        .oe_form_buttons
+            padding: 0
+        .oe-pager-buttons
+            min-height: 30px
+    .oe_view_manager_inline, .oe_view_manager_inlineview
+        > .oe-view-manager-header
+            display: none
     .oe_application 
         > div    
             > div
@@ -985,26 +960,6 @@ $sheet-padding: 16px
                     right: 0
                     overflow: auto
         // }}}
-        // ViewManager application {{{
-    // .oe_view_manager_current
-    //     height: 100%
-    //     > .oe_view_manager_header
-    //         border-bottom: 1px solid #cacaca
-    //         @include vertical-gradient(#fcfcfc, #dedede)
-    //         @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1)))
-    //         .oe_header_row
-    //             td
-    //                 padding: 8px
-    //         .oe_header_row:first-child
-    //             td
-    //                 padding-top: 8px
-    // }}}
-    // ViewManager inline {{{
-    .oe_view_manager_inline, .oe_view_manager_inlineview
-        height: 100%
-        > .oe_view_manager_header
-            display: none
-    // }}}
     // FormPopup {{{
     .oe_popup_form 
         display: table
@@ -1016,77 +971,48 @@ $sheet-padding: 16px
         // End of customize
     .oe_popup_list_pager
         float: right
+    .oe_popup_search
+        width: 50%
+        float: right
     // }}}
     // SearchView {{{
     .oe_searchview
         cursor: text
         position: relative
-        padding: 1px 0
-        min-width: 400px
-        width: 400px
-        @include radius(13px)
-        input, textarea
-            padding: 3px
-            height: 14px
-            font-size: 12px
-            line-height: 18px
-        //Customize searchview input, select
-        input:not([type]), input[type="text"], input[type="number"]
-            width: 156px
-            height: 22px
-        input[type="checkbox"]
-            margin: 3px 3px 3px 4px
-        select
-            margin: 2px 4px 2px 0 
-        //End of customize
-        &.oe_focused
-            border-color: $tag-border-selected
-            @include box-shadow(0 1px 2px $tag-border-selected inset)
-        .oe_searchview_clear
-            cursor: pointer
-            position: absolute
-            top: 3px
-            right: 18px
-            width: 15px
-            height: 24px
+        display: none
+        height: auto
+        padding-top: 1px
+        padding-bottom: 1px
         .oe_searchview_unfold_drawer
             position: absolute
-            top: 0
-            right: 0
-            color: #ccc
+            top: 8px
+            left: 5px
             cursor: pointer
-            &:hover
-                color: #999
-            &:before
-                position: absolute
-                top: 7px
-                right: 18px
-                width: 0
 
         .oe_searchview_search
             cursor: pointer
             position: absolute
-            left: 6px
+            right: 6px
             top: 8px
 
         .oe_searchview_facets
             min-height: 22px
-            margin: 2px 35px 0 20px
+            margin: 2px 20px 0 10px
             *
                 vertical-align: top
                 display: inline-block
                 font-size: 100%
             .oe_searchview_facet
-                height: 20px
                 margin: 1px 0
                 font-size: 11px
                 &:focus
                     outline: none
+                .label
+                    @include radius(0px)
             .oe_searchview_input
                 padding: 0 0 0 6px
                 font-size: 12px
                 height: 16px
-                margin-top: 3px
                 &:focus
                     outline: none
             .oe_searchview_facet
@@ -1095,8 +1021,6 @@ $sheet-padding: 16px
                 padding: 0
                 -webkit-font-smoothing: auto
                 // spacing for opera, FF
-                span
-                    height: 18px
                 &:focus
                     border-color: $tag-border-selected
                     @include box-shadow(0 0 3px 1px $tag-border-selected)
@@ -1110,9 +1034,12 @@ $sheet-padding: 16px
                 .oe_facet_category.oe_i
                     font-size: 16px
                 .oe_facet_value
+                    border-left: 1px solid #afafb6
                     color: #4C4C4C
                     &:last-child
                         padding-right: 16px
+                    &:first-child
+                        border-left: none
                 .oe_facet_remove
                     position: absolute
                     top: 3px
@@ -1135,14 +1062,13 @@ $sheet-padding: 16px
         .oe-autocomplete
             display: none
             position: absolute
-            width: 300px
             background-color: white
+            width: 400px
             border: 1px solid #afafb6
             z-index: 666
             margin-top: 2px
             cursor: default
             @include radius(3px)
-            @include box-shadow(0 1px 4px rgba(0, 0, 0, 0.3))
             ul
                 list-style-type: none
                 padding-left: 0
@@ -1166,180 +1092,112 @@ $sheet-padding: 16px
                 li.oe-separator:last-child
                     display: none
 
-
-    .oe_searchview_drawer_container
-        overflow: auto
-    .oe_searchview_drawer
-        display: none
-        width: 100%
-        cursor: default
-        display: none
-        overflow: hidden
-        border-bottom: 1px solid $tag-border
-        text-align: left
-        padding: 8px 0
-        .badge
-            font-size: 12px
-            line-height: 12px
-        > div:first-child
-            border: none
-            padding-left: 0
-            li:hover:not(.badge)
-                background-color: $hover-background
-        .col-md-5
+    .oe-search-options
+        a
+            padding-left: 25px
+            padding-right: 25px
+            width: 100%
+            -webkit-user-select: none
+            -moz-user-select: none
+            user-select: none
+        .oe-apply-filter
+            margin-left: 25px
+        .oe-add-filter-menu
+            display: none
+        .oe-add-condition
+            width: auto
+            margin-left: 10px
             padding-left: 0
-        dl 
-            margin-bottom: 0
-        dt
-            color: $section-title-color
-            font-size: 13px
-            line-height: 24px
-        dd
-            line-height: 24px
-            font-size: 13px
-            padding-top: 3px
-        h4, h4 *
-            margin: 0 0 0 2px
-            padding-left: 20px
-            cursor: pointer
-            font-weight: normal
-            display: inline-block
-            &:hover
-                background-color: $hover-background
-        h4:before
-            content: "▸ "
-            color: #a3a3a3
-        button
-            margin: 4px 0
-        .button
-            border: none
-            background: transparent
-            padding: 0 2px
-            @include box-shadow(none)
-            @include radius(0)
-        .oe_searchview_section
-            ul
-                margin: 0 8px 
-                padding: 0
-                list-style: none
-                display: inline
-            li
-                display: inline-block
-                cursor: pointer
-                position: relative
-                margin-right: 8px
-                > span
-                    display: inline-block
-                    max-width: 250px
-                    text-overflow: ellipsis
-                    vertical-align: bottom
-                    overflow: hidden
-        form
-            margin-left: 12px
-            p
-                margin: 4px 0
-                line-height: 18px
-            button
-                margin: 0 0 8px -3px // Managed margin-left according bootstrap3
-        .oe_searchview_savefilter
-            form
-                display: none
-        .oe_searchview_custom
+            padding-right: 10px
+        .selected
+            display: block
+            a
+                font-weight: bold
+            a:before
+                font-family: FontAwesome
+                position: absolute
+                left: 6px
+                top: 3px
+                content: "\f00c"
+        .oe-group-selector
+            margin: 3px 25px
+            min-width: calc(100% - 40px)
+            max-width: 250px
+            width: auto
+        .divider
             display: none
-            li
-                cursor: pointer
-                position: relative
-                line-height: 14px
-                margin-right: 0
-                button
-                    position: absolute
-                    top: 0
-                    right: 5px
-                a
-                    margin-left: 10px
-                    position: inherit
-                    visibility: hidden
-                    display: inline-block
-                span:hover:not(.badge)
-                    background-color: $hover-background
-            li:hover a
-                visibility: visible
-            //Customize for searchview label
-            label
-                font-weight: normal
-            //End of Customize
-        .oe_searchview_dashboard
-            form
-                display: none
-                margin-top: 2px
+        .filters-menu
+            .divider
+                display: list-item
+        .closed-menu
+            a:before
+                font-family: FontAwesome
+                position: absolute
+                left: 12px
+                top: 3px
+                content: "\f0da"
+        .open-menu
+            a:before
+                font-family: FontAwesome
+                position: absolute
+                left: 9px
+                top: 3px
+                content: "\f0d7"
 
-        .oe_searchview_advanced
-            overflow: auto
-            form
-                display: none
-                margin-top: 8px
-            button.oe_add_condition:before
-                content: "Z"
-                font-family: "entypoRegular" !important
-                font-size: 24px
-                font-weight: 300 !important
-                margin-right: 4px
-            ul
-                list-style: none
-                padding: 0
-            li
-                position: relative
-                list-style: none
-                margin: 0
+        .oe-select-group
+            margin: 3px 25px
+        .oe-add-group
+            display: none
+        .oe-save-name
+            display: none
+            margin: 3px 25px
+            span
                 white-space: nowrap
-                &:first-child .searchview_extended_prop_or
-                    visibility: hidden
-                    margin-left: -14px
-            .searchview_extended_prop_or
-                opacity: 0.5
-                margin-left: -14px //Customize 'or' in searchview
-        .oe_opened
-            h4:before
-                content: "▾ "
+                input
+                    margin-left: 0
+        .searchview_extended_prop_field
+            display: inline
+            width: calc(100% - 20px)
+        
+        .searchview_extended_prop_op
+            margin: 3px 0
+            width: calc(100% - 20px)
+        .searchview_extended_delete_prop
+            float: right
+            display: inline
+        .searchview_extended_prop_value
+            width: calc(100% - 20px)
+            > select, > input
+                width: calc(100% - 20px)
+            .oe_datepicker_main
+                width: calc(100% - 20px)
+            .oe_datepicker_master
+                width: 100%
+            .fa-calendar
+                margin-left: -21px
+                cursor: pointer
+        span.fa-trash-o
+            position: absolute
+            top: 6px
+            right: 10px
+            cursor: pointer
+        .dropdown-menu
+            > li
                 position: relative
-                top: -1px
-            form
-                display: block
-
-        // delete buttons
-        .oe_searchview_custom_delete, .searchview_extended_delete_prop
+                white-space: nowrap
+            li.oe-filter-condition
+                white-space: normal
+                padding-left: 25px
+                padding-right: 10px
+                margin-bottom: 5px
+        a
             display: inline-block
-            width: 12px
-            height: 12px
-            line-height: 12px
-            padding: 1px
-            color: #8786b7
-            line-height: 8px
-            text-align: center
-            font-weight: bold
-            text-shadow: 0 1px 1px white
-            &:hover
-                text-decoration: none
-                color: white
-                background: #8786b7
-                text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
-                @include radius(2px)
-        .oe_searchview_custom_delete
-            display: none
-            position: absolute
-            bottom: 1px
-            right: 4px
-        .oe_searchview_custom_private, .oe_searchview_custom_public
-            &:hover
-                .oe_searchview_custom_delete
-                    display: inline-block
-        .oe_searchview_custom_public:after
-            content: ","
-            font-family: "entypoRegular" !important
-            font-size: 22px
-            font-weight: 300 !important
-            margin: 0 0 0 4px
-            padding: 0
+
+    input.oe-save-name
+        width: auto
+    .oe_pager_value
+        display: inline-block  
+
     // }}}
     // Views Common {{{
     .oe_view_nocontent
@@ -1674,6 +1532,9 @@ $sheet-padding: 16px
             width: auto
         .oe_datepicker_root
             display: inline-block
+            .datepickerbutton
+                margin-left: 5px
+                cursor: pointer
         .oe_form_required
             input:not([disabled]):not([readonly]), select:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly])
                 background-color: #D2D2FF !important
@@ -2250,6 +2111,8 @@ $sheet-padding: 16px
             content: "[newline]"
     .oe_debug_view
         float: left
+        margin-top: 5px
+        width: auto
     .oe_debug_view_log
         font-size: 95%
         line-height: 1.2em
@@ -2300,8 +2163,6 @@ $sheet-padding: 16px
         background-color: #7c7bad
 
     // Customize for global tags
-    button, body
-        line-height: normal
     h1,h2
         font-weight: bold
     h3
@@ -2337,8 +2198,6 @@ $sheet-padding: 16px
         
 @-moz-document url-prefix()
     .openerp
-        .oe_searchview .oe_searchview_search
-            top: -1px
         .oe_form_field_many2one .oe_m2o_cm_button
             line-height: 18px
         .oe_webclient
index c07a826..29d345b 100644 (file)
@@ -330,18 +330,16 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
             }
         },
         // search button
-        'click i.oe_searchview_search': function (e) {
+        'click div.oe_searchview_search': function (e) {
             e.stopImmediatePropagation();
             this.do_search();
         },
-        'click .oe_searchview_clear': function (e) {
-            e.stopImmediatePropagation();
-            this.query.reset();
-        },
         'click .oe_searchview_unfold_drawer': function (e) {
             e.stopImmediatePropagation();
-            if (this.drawer) 
-                this.drawer.toggle();
+            $(e.target).toggleClass('fa-chevron-right')
+                       .toggleClass('fa-chevron-down');
+            localStorage.visible_search_menu = !(localStorage.visible_search_menu === 'true');
+            this.toggle_buttons();
         },
         'keydown .oe_searchview_input, .oe_searchview_facet': function (e) {
             switch(e.which) {
@@ -379,126 +377,158 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
             disable_custom_filters: false,
         });
         this._super(parent);
+        this.query = undefined;   
         this.dataset = dataset;
-        this.model = dataset.model;
         this.view_id = view_id;
-
+        this.search_fields = [];
+        this.filters = [];
+        this.groupbys = [];
+        this.visible_filters = !!(localStorage.visible_search_menu === 'true');
+        this.input_subviews = []; // for user input in searchbar
         this.defaults = defaults || {};
-        this.has_defaults = !_.isEmpty(this.defaults);
-
-        this.headless = this.options.hidden && !this.has_defaults;
+        this.headless = this.options.hidden &&  _.isEmpty(this.defaults);
+        this.$buttons = this.options.$buttons;
 
-        this.input_subviews = [];
-        this.view_manager = null;
-        this.$view_manager_header = null;
-
-        this.ready = $.Deferred();
-        this.drawer_ready = $.Deferred();
-        this.fields_view_get = $.Deferred();
-        this.drawer = new instance.web.SearchViewDrawer(parent, this);
-
-    },
+        this.filter_menu = undefined;
+        this.groupby_menu = undefined;
+        this.favorite_menu = undefined
+    },    
     start: function() {
-        var self = this;
-        var p = this._super();
-
-        this.$view_manager_header = this.$el.parents(".oe_view_manager_header").first();
-
+        if (this.headless) {
+            this.$el.hide();
+        }
+        this.toggle_visibility(false);
+        this.$facets_container = this.$('div.oe_searchview_facets');
         this.setup_global_completion();
         this.query = new my.SearchQuery()
                 .on('add change reset remove', this.proxy('do_search'))
                 .on('change', this.proxy('renderChangedFacets'))
                 .on('add reset remove', this.proxy('renderFacets'));
+        var load_view = instance.web.fields_view_get({
+            model: this.dataset._model,
+            view_id: this.view_id,
+            view_type: 'search',
+            context: this.dataset.get_context(),
+        });
+        this.$('.oe_searchview_unfold_drawer')
+            .toggleClass('fa-chevron-right', !this.visible_filters)
+            .toggleClass('fa-chevron-down', this.visible_filters);
+        return $.when(this._super(), this.alive($.when(load_view))
+            .then(this.view_loaded.bind(this)));
+    },
+    view_loaded: function (r) {
+        var self = this;
+        this.fields_view_get = r;
+        this.prepare_search_inputs();
+        if (this.$buttons) {
 
-        if (this.options.hidden) {
-            this.$el.hide();
-        }
-        if (this.headless) {
-            this.ready.resolve();
-        } else {
-            var load_view = instance.web.fields_view_get({
-                model: this.dataset._model,
-                view_id: this.view_id,
-                view_type: 'search',
-                context: this.dataset.get_context(),
-            });
-
-            this.alive($.when(load_view)).then(function (r) {
-                self.fields_view_get.resolve(r);
-                return self.search_view_loaded(r);
-            }).fail(function () {
-                self.ready.reject.apply(null, arguments);
-            });
-        }
+            var fields_def = new instance.web.Model(this.dataset.model).call('fields_get', {
+                    context: this.dataset.context
+                });
 
-        var view_manager = this.getParent();
-        while (!(view_manager instanceof instance.web.ViewManager) &&
-                view_manager && view_manager.getParent) {
-            view_manager = view_manager.getParent();
-        }
+            this.groupby_menu = new my.GroupByMenu(this, this.groupbys, fields_def);
+            this.filter_menu = new my.FilterMenu(this, this.filters, fields_def);
+            this.favorite_menu = new my.FavoriteMenu(this, this.query, this.dataset.model);
 
-        if (view_manager) {
-            this.view_manager = view_manager;
-            view_manager.on('switch_mode', this, function (e) {
-                self.drawer.toggle(false);
-            });
+            this.filter_menu.appendTo(this.$buttons);
+            this.groupby_menu.appendTo(this.$buttons);
+            var custom_filters_ready = this.favorite_menu.appendTo(this.$buttons);
         }
-        return $.when(p, this.ready);
+        return $.when(custom_filters_ready).then(this.proxy('set_default_filters'));
     },
-
-    set_drawer: function (drawer) {
-        this.drawer = drawer;
-    },
-
-    toggle_visibility: function (is_visible) {
-        this.$el.toggle(is_visible);
-    },
-
-    show: function () {
-        this.$el.show();
+    set_default_filters: function (a, b) {
+        var self = this,
+            default_custom_filter = this.$buttons && this.favorite_menu.get_default_filter();
+        if (default_custom_filter) {
+            return this.favorite_menu.toggle_filter(default_custom_filter, true);
+        }
+        if (!_.isEmpty(this.defaults)) {
+            var inputs = this.search_fields.concat(this.filters, this.groupbys),
+                defaults = _.invoke(inputs, 'facet_for_defaults', this.defaults);
+            return $.when.apply(null, defaults).then(function () {
+                self.query.reset(_(arguments).compact(), {preventSearch: true});
+            });
+        } 
+        this.query.reset([], {preventSearch: true});
+        return $.when();
     },
-    hide: function () {
-        this.$el.hide();
+    /**
+     * Performs the search view collection of widget data.
+     *
+     * If the collection went well (all fields are valid), then triggers
+     * :js:func:`instance.web.SearchView.on_search`.
+     *
+     * If at least one field failed its validation, triggers
+     * :js:func:`instance.web.SearchView.on_invalid` instead.
+     *
+     * @param [_query]
+     * @param {Object} [options]
+     */
+    do_search: function (_query, options) {
+        if (options && options.preventSearch) {
+            return;
+        }
+        var search = this.build_search_data();
+        this.trigger('search_data', search.domains, search.contexts, search.groupbys);
     },
+    /**
+     * Extract search data from the view's facets.
+     *
+     * Result is an object with 3 (own) properties:
+     *
+     * domains
+     *     Array of domains
+     * contexts
+     *     Array of contexts
+     * groupbys
+     *     Array of domains, in groupby order rather than view order
+     *
+     * @return {Object}
+     */
+    build_search_data: function () {
+        var domains = [], contexts = [], groupbys = [];
 
-    subviewForRoot: function (subview_root) {
-        return _(this.input_subviews).detect(function (subview) {
-            return subview.$el[0] === subview_root;
+        this.query.each(function (facet) {
+            var field = facet.get('field');
+            var domain = field.get_domain(facet);
+            if (domain) {
+                domains.push(domain);
+            }
+            var context = field.get_context(facet);
+            if (context) {
+                contexts.push(context);
+            }
+            var group_by = field.get_groupby(facet);
+            if (group_by) {
+                groupbys.push.apply(groupbys, group_by);
+            }
         });
+        return {
+            domains: domains,
+            contexts: contexts,
+            groupbys: groupbys,
+        };
+    }, 
+    toggle_visibility: function (is_visible) {
+        this.$el.toggle(!this.headless && is_visible);
+        this.$buttons && this.$buttons.toggle(!this.headless && is_visible && this.visible_filters);
     },
-    siblingSubview: function (subview, direction, wrap_around) {
-        var index = _(this.input_subviews).indexOf(subview) + direction;
-        if (wrap_around && index < 0) {
-            index = this.input_subviews.length - 1;
-        } else if (wrap_around && index >= this.input_subviews.length) {
-            index = 0;
-        }
-        return this.input_subviews[index];
-    },
-    focusPreceding: function (subview_root) {
-        return this.siblingSubview(
-            this.subviewForRoot(subview_root), -1, true)
-                .$el.focus();
-    },
-    focusFollowing: function (subview_root) {
-        return this.siblingSubview(
-            this.subviewForRoot(subview_root), +1, true)
-                .$el.focus();
+    toggle_buttons: function (is_visible) {
+        this.visible_filters = is_visible || !this.visible_filters;
+        this.$buttons && this.$buttons.toggle(this.visible_filters);
     },
-
     /**
      * Sets up search view's view-wide auto-completion widget
      */
     setup_global_completion: function () {
         var self = this;
-        this.autocomplete = new instance.web.search.AutoComplete(this, {
+        this.autocomplete = new my.AutoComplete(this, {
             source: this.proxy('complete_global_search'),
             select: this.proxy('select_completion'),
             delay: 0,
-            get_search_string: function () {
-                return self.$('div.oe_searchview_input').text();
+            get_search_string: function () { 
+                return self.$('div.oe_searchview_input').text(); 
             },
-            width: this.$el.width(),
         });
         this.autocomplete.appendTo(this.$el);
     },
@@ -510,7 +540,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
      * @param {Function} resp response callback
      */
     complete_global_search:  function (req, resp) {
-        $.when.apply(null, _(this.drawer.inputs).chain()
+        var inputs = this.search_fields.concat(this.filters, this.groupbys);
+        $.when.apply(null, _(inputs).chain()
             .filter(function (input) { return input.visible(); })
             .invoke('complete', req.term)
             .value()).then(function () {
@@ -520,7 +551,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
                     .value());
                 });
     },
-
     /**
      * Action to perform in case of selection: create a facet (model)
      * and add it to the search collection
@@ -531,30 +561,34 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
      */
     select_completion: function (e, ui) {
         e.preventDefault();
-
         var input_index = _(this.input_subviews).indexOf(
             this.subviewForRoot(
                 this.$('div.oe_searchview_input:focus')[0]));
         this.query.add(ui.item.facet, {at: input_index / 2});
     },
-    childFocused: function () {
-        this.$el.addClass('oe_focused');
+    subviewForRoot: function (subview_root) {
+        return _(this.input_subviews).detect(function (subview) {
+            return subview.$el[0] === subview_root;
+        });
     },
-    childBlurred: function () {
-        var val = this.$el.val();
-        this.$el.val('');
-        this.$el.removeClass('oe_focused')
-                     .trigger('blur');
-        this.autocomplete.close();
+    siblingSubview: function (subview, direction, wrap_around) {
+        var index = _(this.input_subviews).indexOf(subview) + direction;
+        if (wrap_around && index < 0) {
+            index = this.input_subviews.length - 1;
+        } else if (wrap_around && index >= this.input_subviews.length) {
+            index = 0;
+        }
+        return this.input_subviews[index];
     },
-    /**
-     * Call the renderFacets method with the correct arguments.
-     * This is due to the fact that change events are called with two arguments
-     * (model, options) while add, reset and remove events are called with
-     * (collection, model, options) as arguments
-     */
-    renderChangedFacets: function (model, options) {
-        this.renderFacets(undefined, model, options);
+    focusPreceding: function (subview_root) {
+        return this.siblingSubview(
+            this.subviewForRoot(subview_root), -1, true)
+                .$el.focus();
+    },
+    focusFollowing: function (subview_root) {
+        return this.siblingSubview(
+            this.subviewForRoot(subview_root), +1, true)
+                .$el.focus();
     },
     /**
      * @param {openerp.web.search.SearchQuery | undefined} Undefined if event is change
@@ -564,20 +598,19 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
     renderFacets: function (collection, model, options) {
         var self = this;
         var started = [];
-        var $e = this.$('div.oe_searchview_facets');
         _.invoke(this.input_subviews, 'destroy');
         this.input_subviews = [];
 
         var i = new my.InputView(this);
-        started.push(i.appendTo($e));
+        started.push(i.appendTo(this.$facets_container));
         this.input_subviews.push(i);
         this.query.each(function (facet) {
             var f = new my.FacetView(this, facet);
-            started.push(f.appendTo($e));
+            started.push(f.appendTo(self.$facets_container));
             self.input_subviews.push(f);
 
             var i = new my.InputView(this);
-            started.push(i.appendTo($e));
+            started.push(i.appendTo(self.$facets_container));
             self.input_subviews.push(i);
         }, this);
         _.each(this.input_subviews, function (childView) {
@@ -598,320 +631,77 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
             input_to_focus.$el.focus();
         });
     },
-
-    search_view_loaded: function(data) {
-        var self = this;
-        this.fields_view = data;
-        if (data.type !== 'search' ||
-            data.arch.tag !== 'search') {
-                throw new Error(_.str.sprintf(
-                    "Got non-search view after asking for a search view: type %s, arch root %s",
-                    data.type, data.arch.tag));
-        }
-
-        return this.drawer_ready
-            .then(this.proxy('setup_default_query'))
-            .then(function () { 
-                self.trigger("search_view_loaded", data);
-                self.ready.resolve();
-            });
+    childFocused: function () {
+        this.$el.addClass('active');
     },
-    setup_default_query: function () {
-        // Hacky implementation of CustomFilters#facet_for_defaults ensure
-        // CustomFilters will be ready (and CustomFilters#filters will be
-        // correctly filled) by the time this method executes.
-        var custom_filters = this.drawer.custom_filters.filters;
-        if (!this.options.disable_custom_filters && !_(custom_filters).isEmpty()) {
-            // Check for any is_default custom filter
-            var personal_filter = _(custom_filters).find(function (filter) {
-                return filter.user_id && filter.is_default;
-            });
-            if (personal_filter) {
-                this.drawer.custom_filters.toggle_filter(personal_filter, true);
-                return;
-            }
-
-            var global_filter = _(custom_filters).find(function (filter) {
-                return !filter.user_id && filter.is_default;
-            });
-            if (global_filter) {
-                this.drawer.custom_filters.toggle_filter(global_filter, true);
-                return;
-            }
-        }
-        // No custom filter, or no is_default custom filter, apply view defaults
-        this.query.reset(_(arguments).compact(), {preventSearch: true});
+    childBlurred: function () {
+        var val = this.$el.val();
+        this.$el.val('');
+        this.$el.removeClass('active')
+                     .trigger('blur');
+        this.autocomplete.close();
     },
     /**
-     * Extract search data from the view's facets.
-     *
-     * Result is an object with 4 (own) properties:
-     *
-     * errors
-     *     An array of any error generated during data validation and
-     *     extraction, contains the validation error objects
-     * domains
-     *     Array of domains
-     * contexts
-     *     Array of contexts
-     * groupbys
-     *     Array of domains, in groupby order rather than view order
-     *
-     * @return {Object}
+     * Call the renderFacets method with the correct arguments.
+     * This is due to the fact that change events are called with two arguments
+     * (model, options) while add, reset and remove events are called with
+     * (collection, model, options) as arguments
      */
-    build_search_data: function () {
-        var domains = [], contexts = [], groupbys = [], errors = [];
-
-        this.query.each(function (facet) {
-            var field = facet.get('field');
-            try {
-                var domain = field.get_domain(facet);
-                if (domain) {
-                    domains.push(domain);
-                }
-                var context = field.get_context(facet);
-                if (context) {
-                    contexts.push(context);
-                }
-                var group_by = field.get_groupby(facet);
-                if (group_by) {
-                    groupbys.push.apply(groupbys, group_by);
-                }
-            } catch (e) {
-                if (e instanceof instance.web.search.Invalid) {
-                    errors.push(e);
-                } else {
-                    throw e;
+    renderChangedFacets: function (model, options) {
+        this.renderFacets(undefined, model, options);
+    },
+    // it should parse the arch field of the view, instantiate the corresponding 
+    // filters/fields, and put them in the correct variables:
+    // * this.search_fields is a list of all the fields,
+    // * this.filters: groups of filters
+    // * this.group_by: group_bys
+    prepare_search_inputs: function () {
+        var self = this,
+            arch = this.fields_view_get.arch;
+
+        var filters = [].concat.apply([], _.map(arch.children, function (item) {
+            return item.tag !== 'group' ? eval_item(item) : item.children.map(eval_item);
+        }));
+        function eval_item (item) {
+            var category = 'filters';
+            if (item.attrs.context) {
+                var context = instance.web.pyeval.eval('context', item.attrs.context);
+                if (context.group_by) {
+                    category = 'group_by';
                 }
             }
-        });
-        return {
-            domains: domains,
-            contexts: contexts,
-            groupbys: groupbys,
-            errors: errors
-        };
-    }, 
-    /**
-     * Performs the search view collection of widget data.
-     *
-     * If the collection went well (all fields are valid), then triggers
-     * :js:func:`instance.web.SearchView.on_search`.
-     *
-     * If at least one field failed its validation, triggers
-     * :js:func:`instance.web.SearchView.on_invalid` instead.
-     *
-     * @param [_query]
-     * @param {Object} [options]
-     */
-    do_search: function (_query, options) {
-        if (options && options.preventSearch) {
-            return;
-        }
-        var search = this.build_search_data();
-        if (!_.isEmpty(search.errors)) {
-            this.on_invalid(search.errors);
-            return;
-        }
-        this.trigger('search_data', search.domains, search.contexts, search.groupbys);
-    },
-    /**
-     * Triggered after the SearchView has collected all relevant domains and
-     * contexts.
-     *
-     * It is provided with an Array of domains and an Array of contexts, which
-     * may or may not be evaluated (each item can be either a valid domain or
-     * context, or a string to evaluate in order in the sequence)
-     *
-     * It is also passed an array of contexts used for group_by (they are in
-     * the correct order for group_by evaluation, which contexts may not be)
-     *
-     * @event
-     * @param {Array} domains an array of literal domains or domain references
-     * @param {Array} contexts an array of literal contexts or context refs
-     * @param {Array} groupbys ordered contexts which may or may not have group_by keys
-     */
-    /**
-     * Triggered after a validation error in the SearchView fields.
-     *
-     * Error objects have three keys:
-     * * ``field`` is the name of the invalid field
-     * * ``value`` is the invalid value
-     * * ``message`` is the (in)validation message provided by the field
-     *
-     * @event
-     * @param {Array} errors a never-empty array of error objects
-     */
-    on_invalid: function (errors) {
-        this.do_notify(_t("Invalid Search"), _t("triggered from search view"));
-        this.trigger('invalid_search', errors);
-    },
-
-    // The method appendTo is overwrited to be able to insert the drawer anywhere
-    appendTo: function ($searchview_parent, $searchview_drawer_node) {
-        var $searchview_drawer_node = $searchview_drawer_node || $searchview_parent;
-
-        return $.when(
-            this._super($searchview_parent),
-            this.drawer.appendTo($searchview_drawer_node)
-        );
-    },
-
-    destroy: function () {
-        this.drawer.destroy();
-        this.getParent().destroy.call(this);
-    }
-});
-
-instance.web.SearchViewDrawer = instance.web.Widget.extend({
-    template: "SearchViewDrawer",
-
-    init: function(parent, searchview) {
-        this._super(parent);
-        this.searchview = searchview;
-        this.searchview.set_drawer(this);
-        this.ready = searchview.drawer_ready;
-        this.controls = [];
-        this.inputs = [];
-    },
-
-    toggle: function (visibility) {
-        this.$el.toggle(visibility);
-        var $view_manager_body = this.$el.closest('.oe_view_manager_body');
-        if ($view_manager_body.length) {
-            $view_manager_body.scrollTop(0);
+            return {
+                item: item,
+                category: category,
+            }
         }
-    },
-
-    start: function() {
-        var self = this;
-        if (this.searchview.headless) return $.when(this._super(), this.searchview.ready);
-        var filters_ready = this.searchview.fields_view_get
-                                .then(this.proxy('prepare_filters'));
-        return $.when(this._super(), filters_ready).then(function () {
-            var defaults = arguments[1][0];
-            self.ready.resolve.apply(null, defaults);
-        });
-    },
-    prepare_filters: function (data) {
-        this.make_widgets(
-            data['arch'].children,
-            data.fields);
-
-        this.add_common_inputs();
-
-        // build drawer
-        var in_drawer = this.select_for_drawer();
-
-        var $first_col = this.$(".col-md-7"),
-            $snd_col = this.$(".col-md-5");
-
-        var add_custom_filters = in_drawer[0].appendTo($first_col),
-            add_filters = in_drawer[1].appendTo($first_col),
-            add_rest = $.when.apply(null, _(in_drawer.slice(2)).invoke('appendTo', $snd_col)),
-            defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
-                'facet_for_defaults', this.searchview.defaults));
-
-        return $.when(defaults_fetched, add_custom_filters, add_filters, add_rest);
-    },
-    /**
-     * Sets up thingie where all the mess is put?
-     */
-    select_for_drawer: function () {
-        return _(this.inputs).filter(function (input) {
-            return input.in_drawer();
-        });
-    },
+        var current_group = [],
+            current_category = 'filters',
+            categories = {filters: this.filters, group_by: this.groupbys};
 
-    /**
-     * Builds a list of widget rows (each row is an array of widgets)
-     *
-     * @param {Array} items a list of nodes to convert to widgets
-     * @param {Object} fields a mapping of field names to (ORM) field attributes
-     * @param {Object} [group] group to put the new controls in
-     */
-    make_widgets: function (items, fields, group) {
-        if (!group) {
-            group = new instance.web.search.Group(
-                this, 'q', {attrs: {string: _t("Filters")}});
-        }
-        var self = this;
-        var filters = [];
-        _.each(items, function (item) {
-            if (filters.length && item.tag !== 'filter') {
-                group.push(new instance.web.search.FilterGroup(filters, group));
-                filters = [];
+        _.each(filters.concat({category:'filters', item: 'separator'}), function (filter) {
+            if (filter.item.tag === 'filter' && filter.category === current_category) {
+                return current_group.push(new my.Filter(filter.item, self));
             }
-
-            switch (item.tag) {
-            case 'separator': case 'newline':
-                break;
-            case 'filter':
-                filters.push(new instance.web.search.Filter(item, group));
-                break;
-            case 'group':
-                self.add_separator();
-                self.make_widgets(item.children, fields,
-                    new instance.web.search.Group(group, 'w', item));
-                self.add_separator();
-                break;
-            case 'field':
-                var field = this.make_field(
-                    item, fields[item['attrs'].name], group);
-                group.push(field);
-                // filters
-                self.make_widgets(item.children, fields, group);
-                break;
+            if (current_group.length) {
+                var group = new my.FilterGroup(current_group, self);
+                categories[current_category].push(group);
+                current_group = [];
             }
-        }, this);
-
-        if (filters.length) {
-            group.push(new instance.web.search.FilterGroup(filters, this));
-        }
-    },
-
-    add_separator: function () {
-        if (!(_.last(this.inputs) instanceof instance.web.search.Separator))
-            new instance.web.search.Separator(this);
-    },
-    /**
-     * Creates a field for the provided field descriptor item (which comes
-     * from fields_view_get)
-     *
-     * @param {Object} item fields_view_get node for the field
-     * @param {Object} field fields_get result for the field
-     * @param {Object} [parent]
-     * @returns instance.web.search.Field
-     */
-    make_field: function (item, field, parent) {
-        // M2O combined with selection widget is pointless and broken in search views,
-        // but has been used in the past for unsupported hacks -> ignore it
-        if (field.type === "many2one" && item.attrs.widget === "selection"){
-            item.attrs.widget = undefined;
-        }
-        var obj = instance.web.search.fields.get_any( [item.attrs.widget, field.type]);
-        if(obj) {
-            return new (obj) (item, field, parent || this);
-        } else {
-            console.group('Unknown field type ' + field.type);
-            console.error('View node', item);
-            console.info('View field', field);
-            console.info('In view', this);
-            console.groupEnd();
-            return null;
-        }
-    },
-
-    add_common_inputs: function() {
-        // add custom filters to this.inputs
-        this.custom_filters = new instance.web.search.CustomFilters(this);
-        // add Filters to this.inputs, need view.controls filled
-        (new instance.web.search.Filters(this));
-        (new instance.web.search.SaveFilter(this, this.custom_filters));
-        // add Advanced to this.inputs
-        (new instance.web.search.Advanced(this));
+            if (filter.item.tag === 'field') {
+                var attrs = filter.item.attrs,
+                    field = self.fields_view_get.fields[attrs.name],
+                    Obj = my.fields.get_any([attrs.widget, field.type]);
+                if (Obj) {
+                    self.search_fields.push(new (Obj) (filter.item, field, self));
+                }
+            }
+            if (filter.item.tag === 'filter') {
+                current_group.push(new my.Filter(filter.item, self));
+            }
+            current_category = filter.category;
+        });
     },
-
 });
 
 /**
@@ -933,91 +723,17 @@ instance.web.search.fields = new instance.web.Registry({
     'many2many': 'instance.web.search.CharField',
     'one2many': 'instance.web.search.CharField'
 });
-instance.web.search.Invalid = instance.web.Class.extend( /** @lends instance.web.search.Invalid# */{
-    /**
-     * Exception thrown by search widgets when they hold invalid values,
-     * which they can not return when asked.
-     *
-     * @constructs instance.web.search.Invalid
-     * @extends instance.web.Class
-     *
-     * @param field the name of the field holding an invalid value
-     * @param value the invalid value
-     * @param message validation failure message
-     */
-    init: function (field, value, message) {
-        this.field = field;
-        this.value = value;
-        this.message = message;
-    },
-    toString: function () {
-        return _.str.sprintf(
-            _t("Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s"),
-            {fieldname: this.field, value: this.value, message: this.message}
-        );
-    }
-});
-instance.web.search.Widget = instance.web.Widget.extend( /** @lends instance.web.search.Widget# */{
-    template: null,
-    /**
-     * Root class of all search widgets
-     *
-     * @constructs instance.web.search.Widget
-     * @extends instance.web.Widget
-     *
-     * @param parent parent of this widget
-     */
-    init: function (parent) {
-        this._super(parent);
-        var ancestor = parent;
-        do {
-            this.drawer = ancestor;
-        } while (!(ancestor instanceof instance.web.SearchViewDrawer)
-               && (ancestor = (ancestor.getParent && ancestor.getParent())));
-        this.view = this.drawer.searchview || this.drawer;
-    }
-});
 
-instance.web.search.add_expand_listener = function($root) {
-    $root.find('a.searchview_group_string').click(function (e) {
-        $root.toggleClass('folded expanded');
-        e.stopPropagation();
-        e.preventDefault();
-    });
-};
-instance.web.search.Group = instance.web.search.Widget.extend({
-    init: function (parent, icon, node) {
-        this._super(parent);
-        var attrs = node.attrs;
-        this.modifiers = attrs.modifiers =
-            attrs.modifiers ? JSON.parse(attrs.modifiers) : {};
-        this.attrs = attrs;
-        this.icon = icon;
-        this.name = attrs.string;
-        this.children = [];
-
-        this.drawer.controls.push(this);
-    },
-    push: function (input) {
-        this.children.push(input);
-    },
-    visible: function () {
-        return !this.modifiers.invisible;
-    },
-});
-
-instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instance.web.search.Input# */{
-    _in_drawer: false,
+instance.web.search.Input = instance.web.Widget.extend( /** @lends instance.web.search.Input# */{
     /**
      * @constructs instance.web.search.Input
-     * @extends instance.web.search.Widget
+     * @extends instance.web.Widget
      *
      * @param parent
      */
     init: function (parent) {
         this._super(parent);
         this.load_attrs({});
-        this.drawer.inputs.push(this);
     },
     /**
      * Fetch auto-completion values for the widget.
@@ -1049,9 +765,6 @@ instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instan
         }
         return this.facet_for(defaults[this.attrs.name]);
     },
-    in_drawer: function () {
-        return !!this._in_drawer;
-    },
     get_context: function () {
         throw new Error(
             "get_context not implemented for widget " + this.attrs.type);
@@ -1076,23 +789,22 @@ instance.web.search.Input = instance.web.search.Widget.extend( /** @lends instan
      * @returns {Boolean}
      */
     visible: function () {
-        if (this.attrs.modifiers.invisible) {
-            return false;
-        }
-        var parent = this;
-        while ((parent = parent.getParent()) &&
-               (   (parent instanceof instance.web.search.Group)
-                || (parent instanceof instance.web.search.Input))) {
-            if (!parent.visible()) {
-                return false;
-            }
-        }
-        return true;
+        return !this.attrs.modifiers.invisible;
+
+        // var parent = this;
+        // while ((parent = parent.getParent()) &&
+        //        (   (parent instanceof instance.web.search.Group)
+        //         || (parent instanceof instance.web.search.Input))) {
+        //     if (!parent.visible()) {
+        //         return false;
+        //     }
+        // }
+        // return true;
     },
 });
 instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends instance.web.search.FilterGroup# */{
     template: 'SearchView.filters',
-    icon: 'q',
+    icon: "fa-filter",
     completion_label: _lt("Filter on: %s"),
     /**
      * Inclusive group of filters, creates a continuous "button" with clickable
@@ -1116,10 +828,11 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
         }
         this._super(parent);
         this.filters = filters;
-        this.view.query.on('add remove change reset', this.proxy('search_change'));
+        this.searchview = parent;
+        this.searchview.query.on('add remove change reset', this.proxy('search_change'));
     },
     start: function () {
-        this.$el.on('click', 'li', this.proxy('toggle_filter'));
+        this.$el.on('click', 'a', this.proxy('toggle_filter'));
         return $.when(null);
     },
     /**
@@ -1128,15 +841,15 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
      */
     search_change: function () {
         var self = this;
-        var $filters = this.$('> li').removeClass('badge');
-        var facet = this.view.query.find(_.bind(this.match_facet, this));
+        var $filters = this.$el.removeClass('selected');
+        var facet = this.searchview.query.find(_.bind(this.match_facet, this));
         if (!facet) { return; }
         facet.values.each(function (v) {
             var i = _(self.filters).indexOf(v.get('value'));
             if (i === -1) { return; }
             $filters.filter(function () {
                 return Number($(this).data('index')) === i;
-            }).addClass('badge');
+            }).addClass('selected');
         });
     },
     /**
@@ -1226,10 +939,11 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
         });
     },
     toggle_filter: function (e) {
-        this.toggle(this.filters[Number($(e.target).data('index'))]);
+        e.stopPropagation();
+        this.toggle(this.filters[Number($(e.target).parent().data('index'))]);
     },
-    toggle: function (filter) {
-        this.view.query.toggle(this.make_facet([this.make_value(filter)]));
+    toggle: function (filter, options) {
+        this.searchview.query.toggle(this.make_facet([this.make_value(filter)]), options);
     },
     complete: function (item) {
         var self = this;
@@ -1260,10 +974,11 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
     }
 });
 instance.web.search.GroupbyGroup = instance.web.search.FilterGroup.extend({
-    icon: 'w',
+    icon: 'fa-bars',
     completion_label: _lt("Group by: %s"),
     init: function (filters, parent) {
         this._super(filters, parent);
+        this.searchview = parent;
         // Not flanders: facet unicity is handled through the
         // (category, field) pair of facet attributes. This is all well and
         // good for regular filter groups where a group matches a facet, but for
@@ -1271,8 +986,8 @@ instance.web.search.GroupbyGroup = instance.web.search.FilterGroup.extend({
         // view which proxies to the first GroupbyGroup, so it can be used
         // for every GroupbyGroup and still provides the various methods needed
         // by the search view. Use weirdo name to avoid risks of conflicts
-        if (!this.view._s_groupby) {
-            this.view._s_groupby = {
+        if (!this.searchview._s_groupby) {
+            this.searchview._s_groupby = {
                 help: "See GroupbyGroup#init",
                 get_context: this.proxy('get_context'),
                 get_domain: this.proxy('get_domain'),
@@ -1281,14 +996,14 @@ instance.web.search.GroupbyGroup = instance.web.search.FilterGroup.extend({
         }
     },
     match_facet: function (facet) {
-        return facet.get('field') === this.view._s_groupby;
+        return facet.get('field') === this.searchview._s_groupby;
     },
     make_facet: function (values) {
         return {
             category: _t("GroupBy"),
             icon: this.icon,
             values: values,
-            field: this.view._s_groupby
+            field: this.searchview._s_groupby
         };
     }
 });
@@ -1317,14 +1032,6 @@ instance.web.search.Filter = instance.web.search.Input.extend(/** @lends instanc
     get_domain: function () { },
 });
 
-instance.web.search.Separator = instance.web.search.Input.extend({
-    _in_drawer: false,
-
-    complete: function () {
-        return {is_separator: true};
-    }
-});
-
 instance.web.search.Field = instance.web.search.Input.extend( /** @lends instance.web.search.Field# */ {
     template: 'SearchView.field',
     default_operator: '=',
@@ -1499,7 +1206,7 @@ instance.web.search.FloatField = instance.web.search.NumberField.extend(/** @len
 function facet_from(field, pair) {
     return {
         field: field,
-        category: field['attrs'].string,
+        category: field.attrs.string,
         values: [{label: pair[1], value: pair[0]}]
     };
 }
@@ -1614,6 +1321,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
     init: function (view_section, field, parent) {
         this._super(view_section, field, parent);
         this.model = new instance.web.Model(this.attrs.relation);
+        this.searchview = parent;
     },
 
     complete: function (value) {
@@ -1637,7 +1345,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
         var self = this;
         // FIXME: "concurrent" searches (multiple requests, mis-ordered responses)
         var context = instance.web.pyeval.eval(
-            'contexts', [this.view.dataset.get_context()]);
+            'contexts', [this.searchview.dataset.get_context()]);
         return this.model.call('name_search', [], {
             name: needle,
             args: (typeof this.attrs.domain === 'string') ? [] : this.attrs.domain,
@@ -1697,40 +1405,286 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
     }
 });
 
-instance.web.search.CustomFilters = instance.web.search.Input.extend({
-    template: 'SearchView.Custom',
-    _in_drawer: true,
-    init: function () {
-        this.is_ready = $.Deferred();
-        this._super.apply(this,arguments);
+instance.web.search.FilterMenu = instance.web.Widget.extend({
+    template: 'SearchView.FilterMenu',
+    events: {
+        'click .oe-add-filter': function () {
+            this.toggle_custom_filter_menu();
+        },
+        'click li': function (event) {event.stopImmediatePropagation();},
+        'hidden.bs.dropdown': function () {
+            this.toggle_custom_filter_menu(false);
+        },
+        'click .oe-add-condition': 'append_proposition',
+        'click .oe-apply-filter': 'commit_search',
+    },
+    init: function (parent, filters, fields_def) {
+        var self = this;
+        this._super(parent);
+        this.filters = filters || [];
+        this.searchview = parent;
+        this.propositions = [];
+        this.fields_def = fields_def.then(function (data) {
+            var fields = {
+                id: { string: 'ID', type: 'id', searchable: true }
+            };
+            _.each(data, function(field_def, field_name) {
+                if (field_def.selectable !== false && field_name !== 'id') {
+                    fields[field_name] = field_def;
+                }
+            });
+            return fields;
+        });
     },
     start: function () {
         var self = this;
+        this.$menu = this.$('.filters-menu');
+        this.$add_filter = this.$('.oe-add-filter');
+        this.$apply_filter = this.$('.oe-apply-filter');
+        this.$add_filter_menu = this.$('.oe-add-filter-menu');
+        _.each(this.filters, function (group) {
+            group.insertBefore(self.$add_filter);
+            $('<li>').addClass('divider').insertBefore(self.$add_filter);
+        });
+        this.append_proposition().then(function (prop) {
+            prop.$el.hide();
+        });
+    },
+    toggle_custom_filter_menu: function (is_open) {
+        this.$add_filter
+            .toggleClass('closed-menu', !is_open)
+            .toggleClass('open-menu', is_open);
+        this.$add_filter_menu.toggle(is_open);
+        if (this.$add_filter.hasClass('closed-menu') && (!this.propositions.length)) {
+            this.append_proposition();
+        }
+        this.$('.oe-filter-condition').toggle(is_open);
+    },
+    append_proposition: function () {
+        var self = this;
+        return this.fields_def.then(function (fields) {
+            var prop = new instance.web.search.ExtendedSearchProposition(self, fields);
+            self.propositions.push(prop);
+            prop.insertBefore(self.$add_filter_menu);
+            self.$apply_filter.prop('disabled', false);
+            return prop;
+        });
+    },
+    remove_proposition: function (prop) {
+        this.propositions = _.reject(this.propositions, function (p) { return p === prop});
+        if (!this.propositions.length) {
+            this.$apply_filter.prop('disabled', true);
+        }
+        prop.destroy();
+    },
+    commit_search: function () {
+        var self = this,
+            filters = _.invoke(this.propositions, 'get_filter'),
+            filters_widgets = _.map(filters, function (filter) {
+            return new my.Filter(filter, self);
+        });
+        var filter_group = new my.FilterGroup(filters_widgets, this.searchview);
+        filter_group.insertBefore(this.$add_filter);
+        $('<li>').addClass('divider').insertBefore(self.$add_filter);
+        
+        filters_widgets.forEach(function (filter) {
+            self.searchview.query.add(filter_group.make_facet([filter_group.make_value(filter)]), {silent: true});
+        });
+        this.searchview.query.trigger('reset');
+
+        _.invoke(this.propositions, 'destroy');
+        this.propositions = [];
+        this.append_proposition();
+        this.toggle_custom_filter_menu(false);
+    },
+});
+
+instance.web.search.GroupByMenu = instance.web.Widget.extend({
+    template: 'SearchView.GroupByMenu',
+    events: {
+        'click li': function (event) {
+            event.stopImmediatePropagation();
+        },
+        'hidden.bs.dropdown': function () {
+            this.toggle_add_menu(false);
+        },
+        'click .add-custom-group a': function () {
+            this.toggle_add_menu();
+        },
+    },
+    init: function (parent, groups, fields_def) {
+        this._super(parent);
+        this.groups = groups || [];
+        this.groupable_fields = {};
+        this.searchview = parent;
+        this.fields_def = fields_def.then(this.proxy('get_groupable_fields'));
+    },
+    start: function () {
+        var self = this;
+        this.$menu = this.$('.group-by-menu');
+        var divider = this.$menu.find('.divider');
+        _.invoke(this.groups, 'insertBefore', divider);
+        if (this.groups.length) {
+            divider.show();
+        }
+        this.$add_group = this.$menu.find('.add-custom-group');
+        this.fields_def.then(function () {
+            self.$menu.append(QWeb.render('GroupByMenuSelector', self));
+            self.$add_group_menu = self.$('.oe-add-group');
+            self.$group_selector = self.$('.oe-group-selector');
+            self.$('.oe-select-group').click(function (event) {
+                self.toggle_add_menu(false);
+                var field = self.$group_selector.find(':selected').data('name');
+                self.add_groupby_to_menu(field);
+            });            
+        });
+    },
+    get_groupable_fields: function (fields) {
+        var self = this,
+            groupable_types = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];
+
+        _.each(fields, function (field, name) {
+            if (field.store && _.contains(groupable_types, field.type)) {
+                self.groupable_fields[name] = field;
+            }
+        });
+    },
+    toggle_add_menu: function (is_open) {
+        this.$add_group
+            .toggleClass('closed-menu', !is_open)
+            .toggleClass('open-menu', is_open);
+        this.$add_group_menu.toggle(is_open);
+        if (this.$add_group.hasClass('open-menu')) {
+            this.$group_selector.focus();
+        }
+    },
+    add_groupby_to_menu: function (field_name) {
+        var filter = new my.Filter({attrs:{
+            context:"{'group_by':'" + field_name + "''}",
+            name: this.groupable_fields[field_name].string,
+        }}, this.searchview);
+        var group = new my.FilterGroup([filter], this.searchview),
+            divider = this.$('.divider').show();
+        group.insertBefore(divider);
+        group.toggle(filter);
+    },
+});
+
+instance.web.search.FavoriteMenu = instance.web.Widget.extend({
+    template: 'SearchView.FavoriteMenu',
+    events: {
+        'click li': function (event) {
+            event.stopImmediatePropagation();
+        },
+        'click .oe-save-search a': function () {
+            this.toggle_save_menu();
+        },
+        'click .oe-save-name button': 'save_favorite',
+        'hidden.bs.dropdown': function () {
+            this.close_menus();
+        },
+    },
+    init: function (parent, query, target_model) {
+        this._super.apply(this,arguments);
+        this.searchview = parent;
+        this.query = query;
+        this.target_model = target_model;
         this.model = new instance.web.Model('ir.filters');
         this.filters = {};
         this.$filters = {};
-        this.view.query
+        var action = instance.client.action_manager.inner_action;
+        this.action_id = action && action.id;
+    },
+    start: function () {
+        var self = this;
+        this.$save_search = this.$('.oe-save-search');
+        this.$save_name = this.$('.oe-save-name');
+        this.$inputs = this.$save_name.find('input');
+        this.$divider = this.$('.divider');
+
+        var $shared_filter = $(this.$inputs[1]),
+            $default_filter = $(this.$inputs[2]);
+        $shared_filter.click(function () {$default_filter.prop('checked', false)});
+        $default_filter.click(function () {$shared_filter.prop('checked', false)});
+
+        this.query
             .on('remove', function (facet) {
-                if (!facet.get('is_custom_filter')) {
-                    return;
+                if (facet.get('is_custom_filter')) {
+                    self.clear_selection();
                 }
-                self.clear_selection();
             })
             .on('reset', this.proxy('clear_selection'));
-        return this.model.call('get_filters', [this.view.model, this.get_action_id()])
-            .then(this.proxy('set_filters'))
-            .done(function () { self.is_ready.resolve(); })
-            .fail(function () { self.is_ready.reject.apply(self.is_ready, arguments); });
+        return this.model.call('get_filters', [this.target_model, this.action_id])
+            .done(this.proxy('prepare_dropdown_menu'));
+    },
+    prepare_dropdown_menu: function (filters) {
+        filters.map(this.append_filter.bind(this));
+    },
+    toggle_save_menu: function (is_open) {
+        this.$save_search
+            .toggleClass('closed-menu', !is_open)
+            .toggleClass('open-menu', is_open);
+        this.$save_name.toggle(is_open);
+        if (this.$save_search.hasClass('open-menu')) {
+            this.$save_name.find('input').first().focus();
+        }
     },
-    get_action_id: function(){
-        var action = instance.client.action_manager.inner_action;
-        if (action) return action.id;
+    close_menus: function () {
+        this.toggle_save_menu(false);
     },
-    /**
-     * Special implementation delaying defaults until CustomFilters is loaded
-     */
-    facet_for_defaults: function () {
-        return this.is_ready;
+    save_favorite: function () {
+        var self = this,
+            filter_name = this.$inputs[0].value,
+            shared_filter = this.$inputs[1].checked,
+            default_filter = this.$inputs[2].checked;
+        if (!filter_name.length){
+            this.do_warn(_t("Error"), _t("Filter name is required."));
+            this.$inputs.first().focus();
+            return;
+        }
+        var search = this.searchview.build_search_data(),
+            results = instance.web.pyeval.sync_eval_domains_and_contexts({
+                domains: search.domains,
+                contexts: search.contexts,
+                group_by_seq: search.groupbys || [],
+            });
+        if (!_.isEmpty(results.group_by)) {
+            results.context.group_by = results.group_by;
+        }
+        // Don't save user_context keys in the custom filter, otherwise end
+        // up with e.g. wrong uid or lang stored *and used in subsequent
+        // reqs*
+        var ctx = results.context;
+        _(_.keys(instance.session.user_context)).each(function (key) {
+            delete ctx[key];
+        });
+        var filter = {
+            name: filter_name,
+            user_id: shared_filter ? false : instance.session.uid,
+            model_id: this.searchview.dataset.model,
+            context: results.context,
+            domain: results.domain,
+            is_default: default_filter,
+            action_id: this.action_id,
+        };
+        return this.model.call('create_or_replace', [filter]).done(function (id) {
+            filter.id = id;
+            self.toggle_save_menu(false);
+            self.$save_name.find('input').val('').prop('checked', false);
+            self.append_filter(filter);
+            self.toggle_filter(filter, true);
+        });
+    },
+    get_default_filter: function () {
+        var personal_filter = _.find(this.filters, function (filter) {
+            return filter.user_id && filter.is_default;
+        });
+        if (personal_filter) {
+            return personal_filter;
+        }
+        return _.find(this.filters, function (filter) {
+            return !filter.user_id && filter.is_default;
+        });
     },
     /**
      * Generates a mapping key (in the filters and $filter mappings) for the
@@ -1746,9 +1700,9 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
      */
     key_for: function (filter) {
         var user_id = filter.user_id,
-            action_id = filter.action_id;
-        var uid = (user_id instanceof Array) ? user_id[0] : user_id;
-        var act_id = (action_id instanceof Array) ? action_id[0] : action_id;
+            action_id = filter.action_id,
+            uid = (user_id instanceof Array) ? user_id[0] : user_id,
+            act_id = (action_id instanceof Array) ? action_id[0] : action_id;
         return _.str.sprintf('(%s)(%s)%s', uid, act_id, filter.name);
     },
     /**
@@ -1764,264 +1718,82 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
     facet_for: function (filter) {
         return {
             category: _t("Custom Filter"),
-            icon: 'M',
+            icon: 'fa-star',
             field: {
                 get_context: function () { return filter.context; },
                 get_groupby: function () { return [filter.context]; },
                 get_domain: function () { return filter.domain; }
             },
-            _id: filter['id'],
+            _id: filter.id,
             is_custom_filter: true,
             values: [{label: filter.name, value: null}]
         };
     },
     clear_selection: function () {
-        this.$('span.badge').removeClass('badge');
+        this.$('li.selected').removeClass('selected');
     },
     append_filter: function (filter) {
-        var self = this;
-        var key = this.key_for(filter);
-        var warning = _t("This filter is global and will be removed for everybody if you continue.");
+        var self = this,
+            key = this.key_for(filter),
+            $filter,
+            warning = _t("This filter is global and will be removed for everybody if you continue.");
 
-        var $filter;
+        this.$divider.show();
         if (key in this.$filters) {
             $filter = this.$filters[key];
         } else {
-            var id = filter.id;
             this.filters[key] = filter;
             $filter = $('<li></li>')
-                .appendTo(this.$('.oe_searchview_custom_list'))
+                .insertBefore(this.$divider)
                 .toggleClass('oe_searchview_custom_default', filter.is_default)
-                .append(this.$filters[key] = $('<span>').text(filter.name));
+                .append($('<a>').text(filter.name));
 
+            this.$filters[key] = $filter;
             this.$filters[key].addClass(filter.user_id ? 'oe_searchview_custom_private'
                                          : 'oe_searchview_custom_public')
-
-            $('<a class="oe_searchview_custom_delete">x</a>')
-                .click(function (e) {
-                    e.stopPropagation();
-                    if (!(filter.user_id || confirm(warning))) {
-                        return;
-                    }
-                    self.model.call('unlink', [id]).done(function () {
-                        $filter.remove();
-                        delete self.$filters[key];
-                        delete self.filters[key];
-                        if (_.isEmpty(self.filters)) {
-                            self.hide();
-                        }
-                    });
+            $('<span>')
+                .addClass('fa fa-trash-o')
+                .click(function (event) {
+                    event.stopImmediatePropagation(); 
+                    self.remove_filter(filter, $filter, key);
                 })
                 .appendTo($filter);
         }
-
-        this.$filters[key].unbind('click').click(function () {
+        this.$filters[key].unbind('click').click(function (event) {
+            event.stopPropagation();
             self.toggle_filter(filter);
         });
-        this.show();
     },
     toggle_filter: function (filter, preventSearch) {
-        var current = this.view.query.find(function (facet) {
+        var current = this.query.find(function (facet) {
             return facet.get('_id') === filter.id;
         });
         if (current) {
-            this.view.query.remove(current);
-            this.$filters[this.key_for(filter)].removeClass('badge');
+            this.query.remove(current);
+            this.$filters[this.key_for(filter)].removeClass('selected');
             return;
         }
-        this.view.query.reset([this.facet_for(filter)], {
+        this.query.reset([this.facet_for(filter)], {
             preventSearch: preventSearch || false});
-        this.$filters[this.key_for(filter)].addClass('badge');
-    },
-    set_filters: function (filters) {
-        _(filters).map(_.bind(this.append_filter, this));
-        if (!filters.length) {
-            this.hide();
-        }
-    },
-    hide: function () {
-        this.$el.hide();
-    },
-    show: function () {
-        this.$el.show();
-    },
-});
-
-instance.web.search.SaveFilter = instance.web.search.Input.extend({
-    template: 'SearchView.SaveFilter',
-    _in_drawer: true,
-    init: function (parent, custom_filters) {
-        this._super(parent);
-        this.custom_filters = custom_filters;
-    },
-    start: function () {
-        var self = this;
-        this.model = new instance.web.Model('ir.filters');
-        this.$el.on('submit', 'form', this.proxy('save_current'));
-        this.$el.on('click', 'input[type=checkbox]', function() {
-            $(this).siblings('input[type=checkbox]').prop('checked', false);
-        });
-        this.$el.on('click', 'h4', function () {
-            self.$el.toggleClass('oe_opened');
-        });
+        this.$filters[this.key_for(filter)].addClass('selected');
     },
-    save_current: function () {
+    remove_filter: function (filter, $filter, key) {
         var self = this;
-        var $name = this.$('input:first');
-        var private_filter = !this.$('#oe_searchview_custom_public').prop('checked');
-        var set_as_default = this.$('#oe_searchview_custom_default').prop('checked');
-        if (_.isEmpty($name.val())){
-            this.do_warn(_t("Error"), _t("Filter name is required."));
-            return false;
+        var warning = _t("This filter is global and will be removed for everybody if you continue.");
+        if (!(filter.user_id || confirm(warning))) {
+            return;
         }
-        var search = this.view.build_search_data();
-        instance.web.pyeval.eval_domains_and_contexts({
-            domains: search.domains,
-            contexts: search.contexts,
-            group_by_seq: search.groupbys || []
-        }).done(function (results) {
-            if (!_.isEmpty(results.group_by)) {
-                results.context.group_by = results.group_by;
+        this.model.call('unlink', [filter.id]).done(function () {
+            $filter.remove();
+            delete self.$filters[key];
+            delete self.filters[key];
+            if (_.isEmpty(self.filters)) {
+                self.$('li.divider').remove();
             }
-            // Don't save user_context keys in the custom filter, otherwise end
-            // up with e.g. wrong uid or lang stored *and used in subsequent
-            // reqs*
-            var ctx = results.context;
-            _(_.keys(instance.session.user_context)).each(function (key) {
-                delete ctx[key];
-            });
-            var filter = {
-                name: $name.val(),
-                user_id: private_filter ? instance.session.uid : false,
-                model_id: self.view.model,
-                context: results.context,
-                domain: results.domain,
-                is_default: set_as_default,
-                action_id: self.custom_filters.get_action_id()
-            };
-            // FIXME: current context?
-            return self.model.call('create_or_replace', [filter]).done(function (id) {
-                filter.id = id;
-                if (self.custom_filters) {
-                    self.custom_filters.append_filter(filter);
-                }
-                self.$el
-                    .removeClass('oe_opened')
-                    .find('form')[0].reset();
-            });
-        });
-        return false;
-    },
-});
-
-instance.web.search.Filters = instance.web.search.Input.extend({
-    template: 'SearchView.Filters',
-    _in_drawer: true,
-    start: function () {
-        var self = this;
-        var is_group = function (i) { return i instanceof instance.web.search.FilterGroup; };
-        var visible_filters = _(this.drawer.controls).chain().reject(function (group) {
-            return _(_(group.children).filter(is_group)).isEmpty()
-                || group.modifiers.invisible;
-        });
-
-        var groups = visible_filters.map(function (group) {
-                var filters = _(group.children).filter(is_group);
-                return {
-                    name: _.str.sprintf("<span class='oe_i'>%s</span> %s",
-                            group.icon, group.name),
-                    filters: filters,
-                    length: _(filters).chain().map(function (i) {
-                        return i.filters.length; }).sum().value()
-                };
-            }).value();
-
-        var $dl = $('<dl class="dl-horizontal">').appendTo(this.$el);
-
-        var rendered_lines = _.map(groups, function (group) {
-            $('<dt>').html(group.name).appendTo($dl);
-            var $dd = $('<dd>').appendTo($dl);
-            return $.when.apply(null, _(group.filters).invoke('appendTo', $dd));
-        });
-
-        return $.when.apply(this, rendered_lines);
+        });        
     },
 });
 
-instance.web.search.Advanced = instance.web.search.Input.extend({
-    template: 'SearchView.advanced',
-    _in_drawer: true,
-    start: function () {
-        var self = this;
-        this.$el
-            .on('keypress keydown keyup', function (e) { e.stopPropagation(); })
-            .on('click', 'h4', function () {
-                self.$el.toggleClass('oe_opened');
-            }).on('click', 'button.oe_add_condition', function () {
-                self.append_proposition();
-            }).on('submit', 'form', function (e) {
-                e.preventDefault();
-                self.commit_search();
-            });
-        return $.when(
-            this._super(),
-            new instance.web.Model(this.view.model).call('fields_get', {
-                    context: this.view.dataset.context
-                }).done(function(data) {
-                    self.fields = {
-                        id: { string: 'ID', type: 'id', searchable: true }
-                    };
-                    _.each(data, function(field_def, field_name) {
-                        if (field_def.selectable !== false && field_name != 'id') {
-                            self.fields[field_name] = field_def;
-                        }
-                    });
-        })).done(function () {
-            self.append_proposition();
-        });
-    },
-    append_proposition: function () {
-        var self = this;
-        return (new instance.web.search.ExtendedSearchProposition(this, this.fields))
-            .appendTo(this.$('ul')).done(function () {
-                self.$('button.oe_apply').prop('disabled', false);
-            });
-    },
-    remove_proposition: function (prop) {
-        // removing last proposition, disable apply button
-        if (this.getChildren().length <= 1) {
-            this.$('button.oe_apply').prop('disabled', true);
-        }
-        prop.destroy();
-    },
-    commit_search: function () {
-        // Get domain sections from all propositions
-        var children = this.getChildren();
-        var propositions = _.invoke(children, 'get_proposition');
-        var domain = _(propositions).pluck('value');
-        for (var i = domain.length; --i;) {
-            domain.unshift('|');
-        }
-
-        this.view.query.add({
-            category: _t("Advanced"),
-            values: propositions,
-            field: {
-                get_context: function () { },
-                get_domain: function () { return domain;},
-                get_groupby: function () { }
-            }
-        });
-
-        // remove all propositions
-        _.invoke(children, 'destroy');
-        // add new empty proposition
-        this.append_proposition();
-        // TODO: API on searchview
-        this.view.$el.removeClass('oe_searchview_open_drawer');
-    }
-});
-
 instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
     template: 'SearchView.extended_search.proposition',
     events: {
@@ -2030,7 +1802,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
         'click .searchview_extended_delete_prop': function (e) {
             e.stopPropagation();
             this.getParent().remove_proposition(this);
-        }
+        },
     },
     /**
      * @constructs instance.web.search.ExtendedSearchProposition
@@ -2101,18 +1873,22 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @
         this.value.appendTo($value_loc);
 
     },
-    get_proposition: function() {
+    get_filter: function () {
         if (this.attrs.selected === null || this.attrs.selected === undefined)
             return null;
-        var field = this.attrs.selected;
-        var op_select = this.$('.searchview_extended_prop_op')[0];
-        var operator = op_select.options[op_select.selectedIndex];
+        var field = this.attrs.selected,
+            op_select = this.$('.searchview_extended_prop_op')[0],
+            operator = op_select.options[op_select.selectedIndex];
 
         return {
-            label: this.value.get_label(field, operator),
-            value: this.value.get_domain(field, operator),
+            attrs: {
+                domain: [this.value.get_domain(field, operator)],
+                string: this.value.get_label(field, operator),
+            },
+            children: [],
+            tag: 'filter',
         };
-    }
+    },
 });
 
 instance.web.search.ExtendedSearchProposition.Field = instance.web.Widget.extend({
@@ -2337,7 +2113,6 @@ instance.web.search.AutoComplete = instance.web.Widget.extend({
         this.delay = options.delay;
         this.select = options.select,
         this.get_search_string = options.get_search_string;
-        this.width = options.width || 400;
 
         this.current_result = null;
 
@@ -2347,7 +2122,6 @@ instance.web.search.AutoComplete = instance.web.Widget.extend({
     },
     start: function () {
         var self = this;
-        this.$el.width(this.width);
         this.$input.on('keyup', function (ev) {
             if (ev.which === $.ui.keyCode.RIGHT) {
                 self.searching = true;
index 88e6243..2659d46 100644 (file)
@@ -19,9 +19,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
         this.dialog = null;
         this.dialog_widget = null;
         this.view_managers = [];
-        this.on('history_back', this, function() {
-            return this.history_back();
-        });
+        this.on('history_back', this, this.proxy('history_back'));
     },
     dialog_stop: function (reason) {
         if (this.dialog) {
@@ -53,11 +51,20 @@ instance.web.ActionManager = instance.web.Widget.extend({
             var to_destroy = this.view_managers;
             this.view_managers = [];
         }
-        this.view_managers.push(widget);
+        if (widget instanceof instance.web.ViewManager) {
+            this.view_managers.push(widget);
+        } else {
+            this.view_managers.push({
+                view_stack: [{
+                    controller: {get: function () {return action.display_name || action.name; }},
+                }],
+                destroy: function () {},
+            });
+        }
         this.inner_action = action;
         this.inner_widget = widget;
         return $.when(this.inner_widget.appendTo(this.$el)).done(function () {
-            widget.$header && widget.$header.show();
+            (action.target !== 'inline') && (!action.flags.headless) && widget.$header && widget.$header.show();
             old_widget && old_widget.$el.hide();
             if (clear_breadcrumbs) {
                 self.clear_view_managers(to_destroy)
@@ -76,33 +83,32 @@ instance.web.ActionManager = instance.web.Widget.extend({
         }), true);
     },
     history_back: function() {
-        // var last = this.breadcrumbs.slice(-1)[0];
-        // if (!last) {
-        //     return false;
-        // }
-        // var title = last.get_title();
-        // if (_.isArray(title) && title.length > 1) {
-        //     return this.select_breadcrumb(this.breadcrumbs.length - 1, title.length - 2);
-        // } else if (this.breadcrumbs.length === 1) {
-        //     // Only one single titled item in breadcrumb, most of the time you want to trigger back to home
-        //     return false;
-        // } else {
-        //     var prev = this.breadcrumbs[this.breadcrumbs.length - 2];
-        //     title = prev.get_title();
-        //     return this.select_breadcrumb(this.breadcrumbs.length - 2, _.isArray(title) ? title.length - 1 : undefined);
-        // }
+        var view_manager = _.last(this.view_managers),
+            nbr_views = view_manager.view_stack.length;
+        if (nbr_views > 1) {
+            this.select_view_manager(view_manager, nbr_views - 2);
+        } else if (this.view_managers.length > 1) {
+            view_manager = this.view_managers[this.view_managers.length -2];
+            nbr_views = view_manager.view_stack.length;
+            this.select_view(view_managers, nbr_views - 2)
+        }
     },
     select_view_manager: function(view_manager, index) {
         var self = this;
+        if (this.webclient.has_uncommitted_changes()) {
+            return false;
+        }
         var vm_index = this.view_managers.indexOf(view_manager);
-        view_manager.select_view(index).done(function () {
-            _.each(self.view_managers.splice(vm_index + 1), function (vm) {
-                vm.destroy();
+        if (view_manager.select_view) {
+            view_manager.select_view(index).done(function () {
+                _.each(self.view_managers.splice(vm_index + 1), function (vm) {
+                    vm.destroy();
+                });
+                self.inner_widget = _.last(self.view_managers);
+                self.inner_widget.display_breadcrumbs();
+                self.inner_widget.$el.show();
             });
-            self.inner_widget = _.last(self.view_managers);
-            self.inner_widget.display_breadcrumbs();
-            self.inner_widget.$el.show();
-        });
+        }
     },
     clear_view_managers: function(vms) {
         _.each(vms || this.view_managers, function (vm) {
@@ -127,7 +133,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
         // });
     },
     do_push_state: function(state) {
-        if (!this.webclient || !this.webclient.do_push_state) {
+        if (!this.webclient || !this.webclient.do_push_state || this.dialog) {
             return;
         }
         state = state || {};
@@ -168,9 +174,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
                 }
             }
         }
-        if(!this.dialog) {
-            this.webclient.do_push_state(state);
-        }
+        this.webclient.do_push_state(state);
     },
     do_load_state: function(state, warm) {
         var self = this,
@@ -386,6 +390,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
                     $buttons: this.dialog.$buttons,
                     footer_to_buttons: true,
                 });
+                if (widget.action.view_mode === 'form') {
+                    widget.flags.headless = true;
+                }
             }
             this.dialog_widget = widget;
             this.dialog_widget.setParent(this.dialog);
@@ -398,9 +405,6 @@ instance.web.ActionManager = instance.web.Widget.extend({
         }
         widget = executor.widget();
         this.dialog_stop(executor.action);
-        if (options.clear_breadcrumbs) {
-            // this.clear_view_managers();
-        }    
         return this.push_view_manager(widget, executor.action, options.clear_breadcrumbs);
     },
     ir_actions_act_window: function (action, options) {
@@ -412,13 +416,6 @@ instance.web.ActionManager = instance.web.Widget.extend({
             },
             action: action,
             klass: 'oe_act_window',
-            // post_process: function (widget) {
-            //     self.push_breadcrumb({
-            //         widget: widget,
-            //         on_reverse_breadcrumb: options.on_reverse_breadcrumb,
-            //         hide_breadcrumb: options.hide_breadcrumb,
-            //     });
-            // },
         }, options);
     },
     ir_actions_client: function (action, options) {
@@ -440,18 +437,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
             widget: function () { return new ClientWidget(self, action); },
             action: action,
             klass: 'oe_act_client',
-            // post_process: function(widget) {
-            //     self.push_breadcrumb({
-            //         widget: widget,
-            //         title: action.name,
-            //         on_reverse_breadcrumb: options.on_reverse_breadcrumb,
-            //         hide_breadcrumb: options.hide_breadcrumb,
-            //     });
-            //     if (action.tag !== 'reload') {
-            //         self.do_push_state({});
-            //     }
-            // }
-        }, options);
+        }, options).then(function () {
+            if (action.tag !== 'reload') {self.do_push_state({});}
+        });
     },
     ir_actions_act_window_close: function (action, options) {
         if (!this.dialog) {
@@ -544,38 +532,46 @@ instance.web.ViewManager =  instance.web.Widget.extend({
         _.each(views, function (view) {
             var view_type = view[1] || view.view_type,
                 View = instance.web.views.get_object(view_type, true),
-                view_label = View ? View.prototype.display_name : (void 'nope');
-            self.view_order.push(view_type);
-            self.views[view_type] = {
-                controller: null,
-                options: view.options || {},
-                view_id: view[0] || view.view_id,
-                type: view_type,
-                label: view_label,
-                embedded_view: view.embedded_view,
-                title: self.action && self.action.name,
-                button_label: View ? _.str.sprintf(_t('%(view_type)s view'), {'view_type': (view_label || view_type)}) : (void 'nope'),
-            };
+                view_label = View ? View.prototype.display_name: (void 'nope'),
+                view = {
+                    controller: null,
+                    options: view.options || {},
+                    view_id: view[0] || view.view_id,
+                    type: view_type,
+                    label: view_label,
+                    embedded_view: view.embedded_view,
+                    title: self.action && self.action.name,
+                    button_label: View ? _.str.sprintf(_t('%(view_type)s view'), {'view_type': (view_label || view_type)}) : (void 'nope'),
+                };
+            self.view_order.push(view);
+            self.views[view_type] = view;
         });
+        this.multiple_views = (self.view_order.length - ('form' in this.views ? 1 : 0)) > 1;
     },
     /**
      * @returns {jQuery.Deferred} initial view loading promise
      */
     start: function() {
         var self = this;
-        var default_view = this.flags.default_view || this.view_order[0],
+        var default_view = this.flags.default_view || this.view_order[0].type,
             default_options = this.flags[default_view] && this.flags[default_view].options;
 
+        if (this.flags.headless) {
+            this.$('.oe-view-manager-header').hide();
+        }
         this._super();
-        var $sidebar = this.flags.sidebar ? this.$('.oe_view_manager_sidebar') : undefined,
+        var $sidebar = this.flags.sidebar ? this.$('.oe-view-manager-sidebar') : undefined,
             $pager = this.$('.oe-view-manager-pager');
 
         this.$breadcrumbs = this.$('.oe-view-title');
-        this.$switch_buttons = this.$('.oe-view-manager-switch a');
+        this.$switch_buttons = this.$('.oe-view-manager-switch button');
         this.$header = this.$('.oe-view-manager-header');
-
-        self.$switch_buttons.click(function () {
-            self.switch_mode($(this).data('view-type'));
+        this.$header_col = this.$header.find('.oe-header-title');
+        this.$search_col = this.$header.find('.oe-view-manager-search-view');
+        this.$switch_buttons.click(function (event) {
+            if (!$(event.target).hasClass('active')) {
+                self.switch_mode($(this).data('view-type'));
+            }
         });
         var views_ids = {};
         _.each(this.views, function (view) {
@@ -587,21 +583,29 @@ instance.web.ViewManager =  instance.web.Widget.extend({
                 action : self.action,
                 action_views_ids : views_ids,
             }, self.flags, self.flags[view.type], view.options);
+            if (view.type !== 'form') {
+                self.$('.oe-vm-switch-' + view.type).tooltip();
+            }
         });
+        this.$('.oe_debug_view').click(this.on_debug_changed);
+        this.$el.addClass("oe_view_manager_" + ((this.action && this.action.target) || 'current'));
 
-        var search_view_loaded = this.flags.search_view ? this.setup_search_view() : $.when(),
-            main_view_loaded = this.switch_mode(default_view, null, default_options);
+        this.search_view_loaded = this.setup_search_view();
+        var main_view_loaded = this.switch_mode(default_view, null, default_options);
             
-        return $.when(main_view_loaded, search_view_loaded);   
+        return $.when(main_view_loaded, this.search_view_loaded);
     },
 
     switch_mode: function(view_type, no_store, view_options) {
         var self = this,
             view = this.views[view_type];
 
+        if (!view) {
+            return $.Deferred().reject();
+        }
         if (view_type !== 'form') {
             this.view_stack = [];
-        }
+        } 
 
         this.view_stack.push(view);
         this.active_view = view;
@@ -613,7 +617,7 @@ instance.web.ViewManager =  instance.web.Widget.extend({
         if (this.searchview
                 && this.flags.auto_search
                 && view.controller.searchable !== false) {
-            $.when(this.searchview.ready, view.created).done(this.searchview.do_search);
+            $.when(this.search_view_loaded, view.created).done(this.searchview.do_search);
         } else {
             this.active_search.resolve();
         }
@@ -623,6 +627,12 @@ instance.web.ViewManager =  instance.web.Widget.extend({
             self.active_view = view;
             self._display_view(view.type, view_options);
             self.trigger('switch_mode', view_type, no_store, view_options);
+            if (self.session.debug) {
+                self.$('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
+                    view: self.active_view.controller,
+                    view_manager: self,
+                }));
+            }
         });
     },
     update_header: function () {
@@ -642,8 +652,10 @@ instance.web.ViewManager =  instance.web.Widget.extend({
             });
             self.active_view.options.$buttons && self.active_view.options.$buttons.show();
             if (self.searchview) {
-                var is_hidden = self.active_view.controller.searchable === false || self.searchview.options.hidden;
+                var is_hidden = self.active_view.controller.searchable === false;
                 self.searchview.toggle_visibility(!is_hidden);
+                self.$header_col.toggleClass('col-md-6', !is_hidden).toggleClass('col-md-12', is_hidden);
+                self.$search_col.toggle(!is_hidden);
             }
             self.display_breadcrumbs();
         });
@@ -667,7 +679,8 @@ instance.web.ViewManager =  instance.web.Widget.extend({
     create_view: function(view) {
         var self = this,
             View = this.registry.get_object(view.type),
-            options = _.clone(view.options);
+            options = _.clone(view.options),
+            view_loaded = $.Deferred();
 
         if (view.type === "form" && this.action && (this.action.target === 'new' || this.action.target === 'inline')) {
             options.initial_mode = 'edit';
@@ -677,25 +690,26 @@ instance.web.ViewManager =  instance.web.Widget.extend({
 
         $container.hide();
         view.controller = controller;
+        view.$container = $container;
 
         if (view.embedded_view) {
             controller.set_embedded_view(view.embedded_view);
         }
         controller.on('switch_mode', this, this.switch_mode.bind(this));
-
-        view.$container = $container;
-
-        var view_loaded = $.Deferred();
-        view.controller.on('view_loaded', this, function () {
+        controller.on('history_back', this, function () {
+            self.action_manager && self.action_manager.trigger('history_back');
+        });
+        controller.on("change:title", this, function() {
+            self.display_breadcrumbs();
+        });
+        controller.on('view_loaded', this, function () {
             view_loaded.resolve();
         });
-
         return $.when(controller.appendTo($container), view_loaded)
                 .done(function () { 
                     self.trigger("controller_inited", view.type, controller);
                 });
     },
-
     select_view: function (index) {
         var view_type = this.view_stack[index].type;
         this.view_stack.splice(index);
@@ -713,6 +727,7 @@ instance.web.ViewManager =  instance.web.Widget.extend({
         }
 
         var view_id = (this.action && this.action.search_view_id && this.action.search_view_id[0]) || false;
+
         var search_defaults = {};
 
         var context = this.action ? this.action.context : [];
@@ -727,13 +742,13 @@ instance.web.ViewManager =  instance.web.Widget.extend({
         var options = {
             hidden: this.flags.search_view === false,
             disable_custom_filters: this.flags.search_disable_custom_filters,
+            $buttons: this.$('.oe-search-options'),
         };
         var SearchView = instance.web.SearchView;
         this.searchview = new SearchView(this, this.dataset, view_id, search_defaults, options);
 
         this.searchview.on('search_data', this, this.search.bind(this));
-        return this.searchview.appendTo(this.$(".oe-view-manager-search-view:first"),
-                this.$('.oe_searchview_drawer_container'));
+        return this.searchview.appendTo(this.$(".oe-view-manager-search-view:first"));
     },
     search: function(domains, contexts, groupbys) {
         var self = this,
@@ -783,6 +798,145 @@ instance.web.ViewManager =  instance.web.Widget.extend({
             self.active_view.controller.do_load_state(state, warm);
         });
     },
+    on_debug_changed: function (evt) {
+        var self = this,
+            params = $(evt.target).data(),
+            val = params.action,
+            current_view = this.active_view.controller;
+        switch (val) {
+            case 'fvg':
+                var dialog = new instance.web.Dialog(this, { title: _t("Fields View Get") }).open();
+                $('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$el);
+                break;
+            case 'tests':
+                this.do_action({
+                    name: _t("JS Tests"),
+                    target: 'new',
+                    type : 'ir.actions.act_url',
+                    url: '/web/tests?mod=*'
+                });
+                break;
+            case 'get_metadata':
+                var ids = current_view.get_selected_ids();
+                if (ids.length === 1) {
+                    this.dataset.call('get_metadata', [ids]).done(function(result) {
+                        var dialog = new instance.web.Dialog(this, {
+                            title: _.str.sprintf(_t("Metadata (%s)"), self.dataset.model),
+                            size: 'medium',
+                        }, QWeb.render('ViewManagerDebugViewLog', {
+                            perm : result[0],
+                            format : instance.web.format_value
+                        })).open();
+                    });
+                }
+                break;
+            case 'toggle_layout_outline':
+                current_view.rendering_engine.toggle_layout_debugging();
+                break;
+            case 'set_defaults':
+                current_view.open_defaults_dialog();
+                break;
+            case 'translate':
+                this.do_action({
+                    name: _t("Technical Translation"),
+                    res_model : 'ir.translation',
+                    domain : [['type', '!=', 'object'], '|', ['name', '=', this.dataset.model], ['name', 'ilike', this.dataset.model + ',']],
+                    views: [[false, 'list'], [false, 'form']],
+                    type : 'ir.actions.act_window',
+                    view_type : "list",
+                    view_mode : "list"
+                });
+                break;
+            case 'fields':
+                this.dataset.call('fields_get', [false, {}]).done(function (fields) {
+                    var $root = $('<dl>');
+                    _(fields).each(function (attributes, name) {
+                        $root.append($('<dt>').append($('<h4>').text(name)));
+                        var $attrs = $('<dl>').appendTo($('<dd>').appendTo($root));
+                        _(attributes).each(function (def, name) {
+                            if (def instanceof Object) {
+                                def = JSON.stringify(def);
+                            }
+                            $attrs
+                                .append($('<dt>').text(name))
+                                .append($('<dd style="white-space: pre-wrap;">').text(def));
+                        });
+                    });
+                    new instance.web.Dialog(self, {
+                        title: _.str.sprintf(_t("Model %s fields"),
+                                             self.dataset.model),
+                        }, $root).open();
+                });
+                break;
+            case 'edit_workflow':
+                return this.do_action({
+                    res_model : 'workflow',
+                    name: _t('Edit Workflow'),
+                    domain : [['osv', '=', this.dataset.model]],
+                    views: [[false, 'list'], [false, 'form'], [false, 'diagram']],
+                    type : 'ir.actions.act_window',
+                    view_type : 'list',
+                    view_mode : 'list'
+                });
+            case 'edit':
+                this.do_edit_resource(params.model, params.id, evt.target.text);
+                break;
+            case 'manage_filters':
+                this.do_action({
+                    res_model: 'ir.filters',
+                    name: _t('Manage Filters'),
+                    views: [[false, 'list'], [false, 'form']],
+                    type: 'ir.actions.act_window',
+                    context: {
+                        search_default_my_filters: true,
+                        search_default_model_id: this.dataset.model
+                    }
+                });
+                break;
+            case 'print_workflow':
+                if (current_view.get_selected_ids  && current_view.get_selected_ids().length == 1) {
+                    instance.web.blockUI();
+                    var action = {
+                        context: { active_ids: current_view.get_selected_ids() },
+                        report_name: "workflow.instance.graph",
+                        datas: {
+                            model: this.dataset.model,
+                            id: current_view.get_selected_ids()[0],
+                            nested: true,
+                        }
+                    };
+                    this.session.get_file({
+                        url: '/web/report',
+                        data: {action: JSON.stringify(action)},
+                        complete: instance.web.unblockUI
+                    });
+                }
+                break;
+            case 'leave_debug':
+                window.location.search="?";
+                break;
+            default:
+                if (val) {
+                    console.warn("No debug handler for ", val);
+                }
+        }
+    },
+    do_edit_resource: function(model, id, name) {
+        this.do_action({
+            res_model : model,
+            res_id : id,
+            name: name,
+            type : 'ir.actions.act_window',
+            view_type : 'form',
+            view_mode : 'form',
+            views : [[false, 'form']],
+            target : 'new',
+            flags : {
+                action_buttons : true,
+                headless: true,
+            }
+        });
+    },
 });
 
 instance.web.ViewManagerAction = instance.web.ViewManager.extend({
@@ -793,9 +947,25 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
         if (!('auto_search' in flags)) {
             flags.auto_search = action.auto_search !== false;
         }
+        if (action.res_model === 'board.board' && action.view_mode === 'form') {
+            action.target = 'inline';
+            // Special case for Dashboards
+            _.extend(flags, {
+                views_switcher : false,
+                display_title : false,
+                search_view : false,
+                pager : false,
+                sidebar : false,
+                action_buttons : false
+            });
+        }
         this.action = action;
         this.action_manager = parent;
         var dataset = new instance.web.DataSetSearch(this, action.res_model, action.context, action.domain);
+        if (action.res_id) {
+            dataset.ids.push(action.res_id);
+            dataset.index = 0;
+        }
         this._super(parent, dataset, action.views, flags);
     }
 });
@@ -824,7 +994,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
         var self = this;
         this._super(this);
         this.redraw();
-        this.$el.on('click','.oe_dropdown_menu li a', function(event) {
+        this.$el.on('click','.dropdown-menu li a', function(event) {
             var section = $(this).data('section');
             var index = $(this).data('index');
             var item = self.items[section][index];
index e27524c..c452781 100644 (file)
         </div>
     </div>
 </t>
-<t t-name="Tipsy.alert">
-    <a class="oe_tooltip_close oe_e">[</a>
-    <span style="float:left; margin:2px 5px 0 0;" class="ui-icon ui-icon-alert ui-state-error"></span>
-    <div class="oe_tooltip_message">
-        <t t-esc="message"/>
-    </div>
-</t>
 <t t-name="Dialog">
     <div class="modal" tabindex="-1" data-backdrop="static" role="dialog" aria-hidden="true">
         <div class="modal-dialog modal-lg">
     </div>
 </t>
 
-<div t-name="ViewManager">
+<div t-name="ViewManager" class="oe-view-manager">
     <div class="oe-view-manager-header container-fluid">
         <div class="row">
-            <div class="col-md-12 oe-header-title">
+            <div class="col-md-6 oe-header-title">
                 <ol class="oe-view-title breadcrumb" t-if="widget.flags.display_title !== false">
                 </ol>
-                <div class="oe-view-manager-search-view">
-                </div>
-            </div>            
+            </div>
+            <div class="oe-view-manager-search-view col-md-6" />
         </div>
         <div class="row">
-            <div class="col-md-12">
+            <div class="col-sm-6">
                 <div class="oe-view-manager-buttons">
                     <t t-foreach="widget.views" t-as="view">
                         <div t-attf-class="oe-#{view}-buttons"/>
                     </t>                    
                 </div>
                 <div class="oe-view-manager-sidebar"></div>
-
+            </div>
+            <div class="col-sm-6">
+                <div class="oe-search-options btn-group"/>
                 <div class="oe-right-toolbar">
                     <div class="oe-view-manager-pager"></div>
-                    <t t-if="widget.view_order.length > 1">
+                    <t t-if="widget.multiple_views">
                         <div class="oe-view-manager-switch btn-group btn-group-sm">
-                            <t t-foreach="widget.view_order" t-as="view_type">
-                                <a type="button" t-attf-class="btn btn-default fa oe-vm-switch-#{view_type}" t-att-data-view-type="view_type" t-att-title="view_type" t-if="view_type!=='form'">
-                                </a>
+                            <t t-foreach="widget.view_order" t-as="view">
+                                <button type="button" t-attf-class="btn btn-default fa oe-vm-switch-#{view.type}" t-att-data-view-type="view.type" t-att-title="view.label" t-if="view.type!=='form'">
+                                </button>
                             </t>
                         </div>
                     </t>
             </div>
         </div>
     </div>
-    <div class="oe-view-manager-body">
-        <div class="oe_searchview_drawer_container"/>
-        <t t-foreach="widget.views" t-as="view">
-            <div t-attf-class="oe-view-manager-view-#{view}"/>
-        </t>
+    <div class="oe-view-manager-content">
+        <div class="oe-view-manager-body">
+            <div class="oe-view-manager-views">
+                <t t-foreach="widget.views" t-as="view">
+                    <div t-attf-class="oe-view-manager-view-#{view}"/>
+                </t>
+            </div>
+        </div>
     </div>
 </div>
 <t t-name="ViewManagerAction" t-extend="ViewManager">
-    <t t-jquery="h2.oe_view_title" t-operation="before">
-        <select t-if="widget.session.debug" class="oe_debug_view"/>
+    <t t-jquery="ol.oe-view-title" t-operation="before">
+        <div t-if="widget.session.debug" class="oe_debug_view btn-group btn-group-sm"/>
     </t>
 </t>
 <t t-name="ViewManagerDebug">
-    <option value="">Debug View#<t t-esc="view.fields_view.view_id"/></option>
-    <t t-if="view_manager.active_view === 'form'">
-        <option value="get_metadata">View Metadata</option>
-        <option value="toggle_layout_outline">Toggle Form Layout Outline</option>
-        <option value="set_defaults">Set Defaults</option>
-    </t>
-    <option value="tests">JS Tests</option>
-    <option value="fields">View Fields</option>
-    <option value="fvg">Fields View Get</option>
-    <option value="manage_filters">Manage Filters</option>
-    <t t-if="view_manager.session.uid === 1">
-        <option value="translate">Technical translation</option>
-        <option value="manage_views">Manage Views</option>
-        <option value="edit" data-model="ir.ui.view" t-att-data-id="view.fields_view.view_id">Edit <t t-esc="_.str.capitalize(view.fields_view.type)"/>View</option>
-        <option t-if="view_manager.searchview" value="edit" data-model="ir.ui.view" t-att-data-id="view_manager.searchview.view_id">Edit SearchView</option>
-        <option t-if="view_manager.action" value="edit" t-att-data-model="view_manager.action.type" t-att-data-id="view_manager.action.id">Edit Action</option>
-        <option value="edit_workflow">Edit Workflow</option>
-        <option value="print_workflow">Print Workflow</option>
-    </t>
+    <button type="button" class="btn btn-default dropdown-toggle fa fa-bug oe-view-manager-debug" data-toggle="dropdown"> <span class="caret"/>
+    </button>
+    <ul class="dropdown-menu" role="menu">
+        <t t-if="view_manager.active_view.type === 'form'">
+            <li><a data-action="get_metadata">View Metadata</a></li>
+            <li><a data-action="toggle_layout_outline">Toggle Form Layout Outline</a></li>
+            <li><a data-action="set_defaults">Set Defaults</a></li>
+        </t>
+        <li><a data-action="tests">JS Tests</a></li>
+        <li><a data-action="fields">View Fields</a></li>
+        <li><a data-action="fvg">Fields View Get</a></li>
+        <li><a data-action="manage_filters">Manage Filters</a></li>
+        <t t-if="view_manager.session.uid === 1">
+            <li><a data-action="translate">Technical Translation</a></li>
+            <li><a data-action="manage_views">Manage Views</a></li>
+            <li><a data-action="edit" data-model="ir.ui.view" t-att-data-id="view.fields_view.view_id">Edit <t t-esc="_.str.capitalize(view.fields_view.type)"/> View</a></li>
+            <li t-if="view_manager.searchview"><a data-action="edit" data-model="ir.ui.view" t-att-data-id="view_manager.searchview.view_id">Edit SearchView</a></li> 
+            <li t-if="view_manager.action"><a data-action="edit" t-att-data-model="view_manager.action.type" t-att-data-id="view_manager.action.id">Edit Action</a></li> 
+            <li><a data-action="edit_workflow">Edit Workflow</a></li>
+            <li><a data-action="print_workflow">Print Workflow</a></li>
+            <li class="divider"/>
+            <li><a data-action="leave_debug">Leave Debug Mode</a></li>
+        </t>
+    </ul>
 </t>
 <t t-name="ViewManagerDebugViewLog">
     <div class="oe_debug_view_log">
     <div class="oe_pager_value">
         <t t-raw="0"/>
     </div>
-    <div class="btn-group btn-group-sm">
+    <div class="btn-group btn-group-sm oe-pager-buttons">
         <a class="fa fa-chevron-left btn btn-default oe-pager-button" type="button" data-pager-action="previous"></a>
         <a class="fa fa-chevron-right btn btn-default oe-pager-button" type="button" data-pager-action="next"></a>
     </div>
 </div>
 
-<t t-name="Sidebar">
-    <div class="oe_sidebar">
-        <t t-foreach="widget.sections" t-as="section">
-            <div class="oe_form_dropdown_section">
-                <button class="oe_dropdown_toggle oe_dropdown_arrow" t-if="section.name != 'buttons'">
-                    <t t-if="section.name == 'files'" t-raw="widget.items[section.name].length || ''"/>
-                    <t t-esc="section.label"/>
-                    <i class="fa fa-caret-down"/>
+<div t-name="Sidebar" class="oe_sidebar btn-group">
+    <t t-foreach="widget.sections" t-as="section">
+        <div class="oe_form_dropdown_section btn-group btn-group-sm">
+            <button class="btn btn-default dropdown-toggle" 
+                    t-if="section.name != 'buttons'"
+                    data-toggle="dropdown">
+                <t t-if="section.name == 'files'" t-raw="widget.items[section.name].length || ''"/>
+                <t t-esc="section.label"/>
+                <span class="caret"></span>
+            </button>
+            <t t-if="section.name == 'buttons'" t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
+                <button t-att-title="item.title or ''" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url"
+                    target="_blank" class="oe_sidebar_button oe_highlight">
+                    <t t-raw="item.label"/>
                 </button>
-                <t t-if="section.name == 'buttons'" t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
-                    <button t-att-title="item.title or ''" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url"
-                        target="_blank" class="oe_sidebar_button oe_highlight">
-                        <t t-raw="item.label"/>
-                    </button>
-                </t>
-                <ul class="oe_dropdown_menu">
-                    <li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
-                        <t t-if="section.name == 'files'">
-                            <t t-set="item.title">
-                                <b>Attachment : </b><br/>
-                                <t t-raw="item.name"/>
-                            </t>
-                            <t t-if="item.create_uid and item.create_uid[0]" t-set="item.title">
-                                <t t-raw="item.title"/><br/>
-                                <b>Created by : </b><br/>
-                                <t t-raw="item.create_uid[1] + ' ' + item.create_date"/>
-                            </t>
-                            <t t-if="item.create_uid and item.write_uid and item.create_uid[0] != item.write_uid[0]" t-set="item.title">
-                                <t t-raw="item.title"/><br/>
-                                <b>Modified by : </b><br/>
-                                <t t-raw="item.write_uid[1] + ' ' + item.write_date"/>
-                            </t>
+            </t>
+            <ul class="dropdown-menu" role="menu">
+                <li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
+                    <t t-if="section.name == 'files'">
+                        <t t-set="item.title">
+                            <b>Attachment : </b><br/>
+                            <t t-raw="item.name"/>
                         </t>
-                        <a class="oe_sidebar_action_a" t-att-title="item.title or ''" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url" target="_blank">
-                            <t t-raw="item.label"/>
-                        </a>
-                         <a t-if="section.name == 'files' and !item.callback" class="oe_sidebar_delete_item" t-att-data-id="item.id" title="Delete this attachment">x</a>
-                    </li>
-                    <li t-if="section.name == 'files'" class="oe_sidebar_add_attachment">
-                        <t t-call="HiddenInputFile">
-                            <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
-                            <t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
-                            <input type="hidden" name="model" t-att-value="widget.dataset and widget.dataset.model"/>
-                            <input type="hidden" name="id" t-att-value="widget.model_id"/>
-                            <input type="hidden" name="session_id" t-att-value="widget.session.session_id" t-if="widget.session.override_session"/>
-                            <span>Add...</span>
+                        <t t-if="item.create_uid and item.create_uid[0]" t-set="item.title">
+                            <t t-raw="item.title"/><br/>
+                            <b>Created by : </b><br/>
+                            <t t-raw="item.create_uid[1] + ' ' + item.create_date"/>
                         </t>
-                    </li>
-                </ul>
-            </div>
-        </t>
-    </div>
-</t>
+                        <t t-if="item.create_uid and item.write_uid and item.create_uid[0] != item.write_uid[0]" t-set="item.title">
+                            <t t-raw="item.title"/><br/>
+                            <b>Modified by : </b><br/>
+                            <t t-raw="item.write_uid[1] + ' ' + item.write_date"/>
+                        </t>
+                    </t>
+                    <a t-att-title="item.title or ''" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url">
+                        <t t-raw="item.label"/>
+                    </a>
+                     <a t-if="section.name == 'files' and !item.callback" class="oe_sidebar_delete_item" t-att-data-id="item.id" title="Delete this attachment">x</a>
+                </li>
+                <li t-if="section.name == 'files'" class="oe_sidebar_add_attachment">
+                    <t t-call="HiddenInputFile">
+                        <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
+                        <t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
+                        <input type="hidden" name="model" t-att-value="widget.dataset and widget.dataset.model"/>
+                        <input type="hidden" name="id" t-att-value="widget.model_id"/>
+                        <input type="hidden" name="session_id" t-att-value="widget.session.session_id" t-if="widget.session.override_session"/>
+                        <span>Add...</span>
+                    </t>
+                </li>
+            </ul>
+        </div>
+    </t>
+</div>
 
 <t t-name="TreeView">
     <select t-if="toolbar" style="width: 30%">
 <t t-name="ListView.buttons">
     <div class="oe_list_buttons">
     <t t-if="!widget.no_leaf and widget.options.action_buttons !== false and widget.options.addable and widget.is_action_enabled('create')">
-        <button type="button" class="oe_list_add btn btn-primary">
+        <button type="button" class="oe_list_add btn btn-primary btn-sm">
             <t t-esc="widget.options.addable"/>
         </button>
     </t>
         <div class="oe_form_container"/>
     </div>
 </t>
-<div t-name="FormView.buttons" class="oe_form_buttons">
+<div t-name="FormView.buttons">
     <t t-if="widget.options.action_buttons !== false">
-        <span class="oe_form_buttons_view">
-            <!-- required for the bounce effect on button -->
-            <div t-if="widget.is_action_enabled('edit')" style="display: inline-block;">
-                <button type="button" class="oe_form_button_edit btn btn-default" accesskey="E">Edit</button>
-            </div>
+        <div class="btn-group btn-group-sm oe_form_buttons_view">
+            <button t-if="widget.is_action_enabled('edit')" 
+                    type="button" 
+                    class="oe_form_button_edit btn btn-default" accesskey="E">
+                Edit
+            </button>
             <button t-if="widget.is_action_enabled('create')"
-                type="button" class="oe_form_button_create btn btn-default" accesskey="C">Create</button>
-        </span>
+                    type="button" class="oe_form_button_create btn btn-default"
+                    accesskey="C">
+                Create
+            </button>
+        </div>
         <span class="oe_form_buttons_edit">
-            <button type="button" class="oe_form_button_save btn btn-primary" accesskey="S">Save</button>
+            <button type="button" class="oe_form_button_save btn btn-primary btn-sm" accesskey="S">Save</button>
             <span class="oe_fade">or</span>
             <a href="#" class="oe_bold oe_form_button_cancel" accesskey="D">Discard</a>
         </span>
     </label>
 </t>
 
+<div t-name="PivotView" class="test"/>
+
+<t t-name="PivotView.buttons">
+    <div class="btn-group btn-group-sm">
+        <button class="btn btn-default fa fa-expand oe-pivot"></button>
+        <button class="btn btn-default fa fa-arrows-alt oe-pivot"></button>
+        <button class="btn btn-default fa fa-download oe-pivot"></button>
+    </div>
+    <div class="btn-group btn-group-sm"> 
+        <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+            Measures <span class="caret"/>
+        </button>
+        <ul class="oe-measure-list dropdown-menu">
+            <li t-foreach="measures" t-as="measure" t-att-data-field="measure">
+                <a><t t-esc="measures[measure].string"/></a>
+            </li>
+        </ul>
+    </div>
+</t>
+
+
 <t t-name="Widget">
     Unhandled widget
     <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t>
                 t-att-name="widget.name"
                 t-att-placeholder="placeholder"
                 class="oe_datepicker_master"
-            /><span><img class="oe_input_icon oe_datepicker_trigger datepickerbutton" draggable="false"
-                   t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"'
-                   title="Select date" width="16" height="16" border="0"/></span>
+            /><span class="fa fa-calendar datepickerbutton"/>
         </div>
     </span>
 </t>
         <table style="width:100%">
             <tr style="width:100%">
                 <td style="width:100%">
-                    <div class="oe_popup_search" style="width:100%"></div>
+                    <div class="oe-search-options btn-group"/>
+                    <div class="oe_popup_search"></div>
                 </td>
             </tr>
             <tr style="width:100%">
 </t>
 
 <div t-name="SearchView" class="oe_searchview form-control input-sm">
+    <div class="oe_searchview_unfold_drawer fa fa-lg fa-fw fa-chevron-right" title="Advanced Search..."/>
     <div class="oe_searchview_facets"/>
-    <div class="oe_searchview_clear"><i class="fa fa-times fa-lg"></i></div>
-    <div class="oe_searchview_unfold_drawer fa fa-lg fa-chevron-down" title="Advanced Search..."/>
-    <i type="button" class="oe_searchview_search fa fa-lg fa-search"
-            title="Search Again"></i>
-</div>
-
-<div t-name="SearchViewDrawer" class="oe_searchview_drawer" style="display:none;">
-    <div class="col-md-7"></div>
-    <div class="col-md-5"></div>
+    <div class="oe_searchview_search fa fa-lg fa-search" title="Search Again"/>
 </div>
 
 <div t-name="SearchView.InputView"
      class="oe_tag oe_searchview_facet"
      tabindex="0">
     <span class="oe_facet_remove">x</span>
-    <span class="label label-default oe_i" t-if="widget.model.has('icon')">
-        <t t-esc="widget.model.get('icon')"/>
+    <span t-if="widget.model.has('icon')"
+            t-att-class="'label label-default fa ' + widget.model.get('icon')">
     </span>
     <span class="label label-default" t-if="!widget.model.has('icon')">
         <t t-esc="widget.model.get('category')"/>
 <span t-name="SearchView.FacetView.Value" class="oe_facet_value">
     <t t-esc="widget.model.get('label')"/>
 </span>
-<t t-name="SearchView.managed-filters">
-    <option class="oe_search_filters_title" value="">Filters</option>
-    <optgroup label="-- Filters --">
-        <t t-foreach="filters" t-as="filter">
-            <option t-attf-value="get:#{filter_index}"
-                    t-att-disabled="filter.disabled and 'disabled'"
-                    t-att-title="filter.disabled and disabled_filter_message">
-                <t t-esc="filter.name"/>
-            </option>
-        </t>
-    </optgroup>
-    <optgroup label="-- Actions --">
-        <option value="advanced_filter">Add Advanced Filter</option>
-        <option value="save_filter">Save Filter</option>
-        <option value="manage_filters">Manage Filters</option>
-    </optgroup>
-</t>
-<t t-name="SearchView.managed-filters.add">
-    <div>
-        <p>Filter Name:</p>
-        <input type="text"/>
-        <p>(Any existing filter with the same name will be replaced)</p>
-    </div>
-</t>
-<t t-name="SearchView.render_lines">
-    <table class="oe_search_render_line" border="0" cellspacing="0" cellpadding="0"
-           t-foreach="lines" t-as="line">
-        <tr>
-            <td t-foreach="line" t-as="widget" class="oe_searchview_field">
-                <t t-raw="widget.render(defaults)"/>
-            </td>
-        </tr>
-    </table>
-</t>
-<button t-name="SearchView.filter" type="button"
-        t-att-id="element_id"
-        t-att-title="attrs.help"
-        t-att-class="classes.join(' ')"
-        t-att-style="style"
-        t-att-autofocus="attrs.default_focus === '1' ? 'autofocus' : undefined">
-    <img t-att-src="_s + '/web/static/src/img/icons/' + (attrs.icon || 'gtk-home') + '.png'" width="16" height="16"/>
-    <br t-if="attrs.string"/>
-    <t t-esc="attrs.string"/>
-</button>
-<ul t-name="SearchView.filters">
+<t t-name="SearchView.filters">
     <li t-foreach="widget.filters" t-as="filter" t-if="!filter.visible || filter.visible()"
             t-att-title="filter.attrs.string ? filter.attrs.help : undefined"
             t-att-data-index="filter_index">
-        <t t-esc="filter.attrs.string or filter.attrs.help or filter.attrs.name or 'Ω'"/>
+        <a>
+            <t t-esc="filter.attrs.string or filter.attrs.help or filter.attrs.name or 'Ω'"/>
+        </a>
     </li>
-</ul>
-<t t-name="SearchView.filters.facet">
-    <div class="category oe_filter_category"><t t-esc="facet.get('category')"/></div>
-
-    <t t-set="val" t-value="facet.get('json')"/>
-
-    <div t-if="!(val instanceof Array)" class="search_facet_input_container">
-        <t t-esc="facet.get('value')"/>
-    </div>
-    <t t-if="val instanceof Array">
-        <div class="search_facet_input_container"
-                t-foreach="facet.get('json')" t-as="filter">
-            <t t-esc="filter.attrs.string || filter.attrs.name"/>
-        </div>
-    </t>
-
-    <div class="search_facet_remove VS-icon VS-icon-cancel"/>
 </t>
 <t t-name="SearchView.field">
     <label t-att-class="'oe_label' + (attrs.help ? '_help' : '')"
         <t t-if="filters.length" t-raw="filters.render(defaults)"/>
     </div>
 </t>
-<t t-name="SearchView.util.expand">
-    <div t-att-class="'searchview_group ' + (expand == '0' ? 'folded' : 'expanded')">
-        <a t-if="label" class="searchview_group_string" href="#">
-            <t t-esc="label"/>
-        </a>
-        <div class="searchview_group_content">
-            <t t-raw="content"/>
-        </div>
-    </div>
-</t>
-<div t-name="SearchView.Filters" class="oe_searchview_filters oe_searchview_section">
-
-</div>
-
-
-<div t-name="SearchView.Custom" class="oe_searchview_custom oe_searchview_section">
-    <dl class="dl-horizontal">
-        <dt><span class="oe_i">M</span> Favorites</dt>
-        <dd><ul class="oe_searchview_custom_list"/></dd>
-    </dl>
+<div t-name="SearchView.FilterMenu" class="btn-group btn-group-sm">
+    <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+        <span class="fa fa-filter"/> Filters <span class="caret"/>
+    </button>
+    <ul class="dropdown-menu filters-menu" role="menu">
+        <li class="oe-add-filter closed-menu"><a>Add Custom Filter</a></li>
+        <li class="oe-add-filter-menu"><button class="btn btn-default oe-apply-filter">Apply</button><a class="oe-add-condition"><span class="fa fa-plus-circle"/> Add a condition</a></li>
+    </ul>
 </div>
 
- <div t-name="SearchView.SaveFilter" class="oe_searchview_savefilter">
-    <h4>Save current filter</h4>
-    <form class="oe_form">
-        <p class="oe_form_required"><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
-        <p>
-            <input id="oe_searchview_custom_public" type="checkbox"/>
-            <label for="oe_searchview_custom_public">Share with all users</label>
-            <input id="oe_searchview_custom_default" type="checkbox"/>
-            <label for="oe_searchview_custom_default">Use by default</label>
-        </p>
-        <button>Save</button>
-    </form>
+<div t-name="SearchView.GroupByMenu" class="btn-group btn-group-sm">
+    <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+        <span class="fa fa-bars"/> Group By <span class="caret"/>
+    </button>
+    <ul class="dropdown-menu group-by-menu" role="menu">
+        <li class="divider"/>
+        <li class="add-custom-group closed-menu"><a>Add custom group</a></li>
+    </ul>
 </div>
 
-<div t-name="SearchView.advanced" class="oe_searchview_advanced">
-    <h4>Advanced Search</h4>
-    <form>
-        <ul>
-
-        </ul>
-        <button class="oe_add_condition button" type="button">Add a condition</button><br/>
-        <button class="oe_apply" type="submit">Apply</button>
-    </form>
+<t t-name="GroupByMenuSelector">
+    <li><select class="form-control oe-add-group oe-group-selector">
+        <t t-foreach="groupable_fields" t-as="field">
+            <option t-att-data-name="field"><t t-esc="groupable_fields[field].string"/></option>
+        </t>
+    </select></li>
+    <li><button class="btn btn-default oe-add-group oe-select-group">Apply</button></li>
+</t>
+<div t-name="SearchView.FavoriteMenu" class="btn-group btn-group-sm">
+    <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+        <span class="fa fa-star"/> Favorites <span class="caret"/>
+    </button>
+    <ul class="dropdown-menu favorites-menu" role="menu">
+        <li class="divider"/>
+        <li class="oe-save-search closed-menu"><a>Save current search</a></li>
+        <li class="oe-save-name"><input type="text" class="form-control"></input></li>
+        <li class="oe-save-name">
+            <span><input type="checkbox"/>Share with all users</span>
+        </li>
+        <li class="oe-save-name">
+            <span><input type="checkbox"/>Use by default</span>
+        </li>
+        <li class="oe-save-name"><button class="btn btn-default">Save</button></li>
+    </ul>
 </div>
 <t t-name="SearchView.extended_search.proposition">
-    <li>
-        <span class="searchview_extended_prop_or">or</span>
-        <select class="searchview_extended_prop_field">
-            <t t-foreach="widget.attrs.fields" t-as="field">
-                <option t-att="{'selected': field === widget.attrs.selected ? 'selected' : null}"
-                        t-att-value="field.name">
-                    <t t-esc="field.string"/>
-                </option>
-            </t>
-        </select>
-        <select class="searchview_extended_prop_op"/>
+    <li class="oe-filter-condition">
+        <span>
+            <select class="searchview_extended_prop_field form-control">
+                <t t-foreach="widget.attrs.fields" t-as="field">
+                    <option t-att="{'selected': field === widget.attrs.selected ? 'selected' : null}"
+                            t-att-value="field.name">
+                        <t t-esc="field.string"/>
+                    </option>
+                </t>
+            </select>
+            <span class="searchview_extended_delete_prop fa fa-trash-o"/>
+        </span>
+        <select class="searchview_extended_prop_op form-control"/>
         <span class="searchview_extended_prop_value"/>
-        <a class="searchview_extended_delete_prop button">x</a>
     </li>
 </t>
 <t t-name="SearchView.extended_search.proposition.char">
-    <input class="field_char"/>
+    <input class="field_char form-control"/>
 </t>
 <t t-name="SearchView.extended_search.proposition.empty">
     <span/>
 </t>
 <t t-name="SearchView.extended_search.proposition.integer">
-    <input type="number" class="field_integer" value = "0" step="1"/>
+    <input type="number" class="field_integer form-control" value = "0" step="1"/>
 </t>
 <t t-name="SearchView.extended_search.proposition.float">
-    <input type="number" class="field_float" value = "0.0" step="0.01"/>
+    <input type="number" class="field_float form-control" value = "0.0" step="0.01"/>
 </t>
 <t t-name="SearchView.extended_search.proposition.selection">
-    <select>
+    <select class="form-control">
         <t t-foreach="widget.field.selection" t-as="element">
         <option t-att-value="element[0]"><t t-esc="element[1]"/></option>
         </t>