diff --git a/etemplate/js/et2_dataview_view_tile.js b/etemplate/js/et2_dataview_view_tile.js index 57fbf278c2..a4f67d55d6 100644 --- a/etemplate/js/et2_dataview_view_tile.js +++ b/etemplate/js/et2_dataview_view_tile.js @@ -100,7 +100,7 @@ var et2_dataview_tile = et2_dataview_row.extend([], _recalculate_columns: function() { if(this._inTree && this.tr && this.tr.parent()) { - this.columns = parseInt(this.tr.parent().innerWidth() / this.tr.outerWidth(true)); + this.columns = Math.max(1,parseInt(this.tr.parent().innerWidth() / this.tr.outerWidth(true))); } } }); diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index 34cb8000e2..5f4888390e 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -113,7 +113,12 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], "description": "Hide the second filter", "default": et2_no_init }, - + "view": { + "name": "View", + "type": "string", + "description": "Display entries as either 'row' or 'tile'. A matching template must also be set after changing this.", + "default": et2_no_init + }, "onselect": { "name": "onselect", "type": "js", @@ -1663,7 +1668,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], * This should be followed by a call to change the template to match, which * will cause a reload of the grid using the new settings. * - * @param {type} view + * @param {string} view Either 'tile' or 'row' */ set_view: function(view) { diff --git a/filemanager/inc/class.filemanager_favorite_portlet.inc.php b/filemanager/inc/class.filemanager_favorite_portlet.inc.php index fa646a8dd2..f26dd92e8d 100644 --- a/filemanager/inc/class.filemanager_favorite_portlet.inc.php +++ b/filemanager/inc/class.filemanager_favorite_portlet.inc.php @@ -30,12 +30,11 @@ class filemanager_favorite_portlet extends home_favorite_portlet $ui = new filemanager_ui(); - $this->context['template'] = 'filemanager.index.rows'; $this->nm_settings += array( 'get_rows' => 'filemanager.filemanager_ui.get_rows', 'csv_export' => true, // Use a different template so it can be accessed from client side - 'template' => 'filemanager.home.rows', + 'template' => ($this->nm_settings['view'] == 'tile' ? 'filemanager.tile' : 'filemanager.home.rows' ), // Filemanager needs this header, it's an important component for actions, but we reduce it to the minimum 'header_left' => 'filemanager.home.header_left', // Use a reduced column set for home, user can change if needed @@ -80,6 +79,11 @@ class filemanager_favorite_portlet extends home_favorite_portlet { $ui = new filemanager_ui(); $total = $ui->get_rows($query, $rows, $readonlys); + // Change template to match selected view + if($query['view']) + { + $query['template'] = ($query['view'] == 'row' ? 'filemanager.home.rows' : 'filemanager.tile'); + } unset($GLOBALS['egw_info']['flags']['app_header']); return $total; } diff --git a/filemanager/inc/class.filemanager_ui.inc.php b/filemanager/inc/class.filemanager_ui.inc.php index 26bf87ee7c..0958ca310b 100644 --- a/filemanager/inc/class.filemanager_ui.inc.php +++ b/filemanager/inc/class.filemanager_ui.inc.php @@ -482,6 +482,13 @@ class filemanager_ui { $tpl->setElementAttribute('nm[buttons][upload]', 'drop_target', 'popupMainDiv'); } + // Set view button to match current settings + if($content['nm']['view'] == 'tile') + { + $tpl->setElementAttribute('nm[buttons][button][change_view]','label',lang('List view')); + $tpl->setElementAttribute('nm[buttons][button][change_view]','image','list_row'); + + } // if initial load is done via GET request (idots template or share.php) // get_rows cant call app.filemanager.set_readonly, so we need to do that here $content['initial_path_readonly'] = !egw_vfs::is_writable($content['nm']['path']); @@ -757,7 +764,7 @@ class filemanager_ui * @param array $query * @param array &$rows */ - function get_rows($query, &$rows) + function get_rows(&$query, &$rows) { // show projectmanager sidebox for projectmanager path if (substr($query['path'],0,20) == '/apps/projectmanager' && isset($GLOBALS['egw_info']['user']['apps']['projectmanager'])) @@ -771,6 +778,11 @@ class filemanager_ui } if(!$query['path']) $query['path'] = static::get_home_dir(); + // Change template to match selected view + if($query['view']) + { + $query['template'] = ($query['view'] == 'row' ? 'filemanager.index.rows' : 'filemanager.tile'); + } // be tolerant with (in previous versions) not correct urlencoded pathes if (!egw_vfs::stat($query['path'],true) && egw_vfs::stat(urldecode($query['path']))) { diff --git a/filemanager/js/app.js b/filemanager/js/app.js index 9d574d45af..1a9cad73eb 100644 --- a/filemanager/js/app.js +++ b/filemanager/js/app.js @@ -72,6 +72,13 @@ app.classes.filemanager = AppJS.extend( this._super.apply(this, arguments); this.path_widget[et2.DOMContainer.id] = this.et2.getWidgetById('path') || null; + if(this.path_widget[et2.DOMContainer.id]) + { + // Bind to removal to remove from list + $j(et2.DOMContainer).on('clear', function(e) { + delete app.filemanager.path_widget[e.target.id]; + }); + } if(this.et2.getWidgetById('nm')) { @@ -95,6 +102,60 @@ app.classes.filemanager = AppJS.extend( } }, + /** + * Set the application's state to the given state. + * + * Extended from parent to also handle view + * + * + * @param {{name: string, state: object}|string} state Object (or JSON string) for a state. + * Only state is required, and its contents are application specific. + * + * @return {boolean} false - Returns false to stop event propagation + */ + setState: function(state) + { + // State should be an object, not a string, but we'll parse + if(typeof state == "string") + { + if(state.indexOf('{') != -1 || state =='null') + { + state = JSON.parse(state); + } + } + if(typeof state == "object" && state.state && state.state.view) + { + var et2 = etemplate2.getById('filemanager-index'); + if(et2) + { + var nm = et2.widgetContainer.getWidgetById('nm'); + nm.set_view(state.state.view); + } + } + return this._super.call(this,state); + }, + + /** + * Retrieve the current state of the application for future restoration + * + * Extended from parent to also set view + * + * @return {object} Application specific map representing the current state + */ + getState: function() + { + var state = this._super.apply(this); + + var et2 = etemplate2.getById('filemanager-index'); + if(et2) + { + var nm = et2.widgetContainer.getWidgetById('nm'); + state.view = nm.view; + } + + return state; + }, + /** * Regexp to convert id to a path, use this.id2path(_id) */ @@ -609,7 +670,8 @@ app.classes.filemanager = AppJS.extend( /** * Toggle view between tiles and rows * - * @param {string} [view] - Specify what to change the view to. Either 'tile' or 'row'. + * @param {string|Event} [view] - Specify what to change the view to. Either 'tile' or 'row'. + * Or, if this is used as a callback view is actually the event, and we need to find the view. * @param {et2_widget} [button_widget] - The widget that's calling */ change_view: function(view, button_widget) @@ -622,6 +684,10 @@ app.classes.filemanager = AppJS.extend( return; } + if(!button_widget) + { + button_widget = nm.getWidgetById('button[change_view]'); + } if(button_widget && button_widget.instanceOf(et2_button)) { // Switch view based on button icon, since controller can get re-created @@ -637,6 +703,8 @@ app.classes.filemanager = AppJS.extend( } nm.set_view(view); + // Put it into active filters (but don't refresh) + nm.activeFilters.view = view; // Change template to match this.et2.getWidgetById('nm').set_template(view == nm.controller.VIEW_ROW ? 'filemanager.index.rows' : 'filemanager.tile'); @@ -765,23 +833,26 @@ app.classes.filemanager = AppJS.extend( this.readonly = [_path, _ro]; return; } - var path = this.get_path(); - - if (_path == path) + for(var id in this.path_widget) { - var ids = ['button[linkpaste]', 'button[paste]', 'button[createdir]', 'button[symlink]', 'upload']; - for(var i=0; i < ids.length; ++i) + var path = this.get_path(id); + + if (_path == path) { - var widget = this.et2.getWidgetById(ids[i]); - if (widget) + var ids = ['button[linkpaste]', 'button[paste]', 'button[createdir]', 'button[symlink]', 'upload']; + for(var i=0; i < ids.length; ++i) { - if (widget._type == 'button' || widget._type == 'buttononly') + var widget = etemplate2.getById(id).widgetContainer.getWidgetById(ids[i]); + if (widget) { - widget.set_readonly(_ro); - } - else - { - widget.set_disabled(_ro); + if (widget._type == 'button' || widget._type == 'buttononly') + { + widget.set_readonly(_ro); + } + else + { + widget.set_disabled(_ro); + } } } } diff --git a/filemanager/templates/default/app.css b/filemanager/templates/default/app.css index fae8ca06e4..cb41b4a941 100644 --- a/filemanager/templates/default/app.css +++ b/filemanager/templates/default/app.css @@ -73,20 +73,15 @@ table.egwGridView_grid .tile .file_tile { display: block; max-width: 140px; } -#filemanager-index_nm .tile .file_tile img.vfsMimeIcon { +.tile .file_tile img.vfsMimeIcon { height: auto; width: auto; max-height: 64px; display:block; margin: 0 auto; } -.egwGridView_grid tr.tile:hover .innerContainer { - overflow: visible; -} -.egwGridView_grid tr.tile:hover .file_tile > :not(.iconOverlayContainer) { - position: relative; - z-index:90; - background-color: white; +.tile .file_tile .et2_label { + word-wrap: break-word; } /** diff --git a/filemanager/templates/default/home.header_left.xet b/filemanager/templates/default/home.header_left.xet new file mode 100644 index 0000000000..298d8b9293 --- /dev/null +++ b/filemanager/templates/default/home.header_left.xet @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/filemanager/templates/default/tile.xet b/filemanager/templates/default/tile.xet index fb0e2ee549..1e0791b2c5 100644 --- a/filemanager/templates/default/tile.xet +++ b/filemanager/templates/default/tile.xet @@ -18,7 +18,7 @@ - + diff --git a/filemanager/templates/pixelegg/app.css b/filemanager/templates/pixelegg/app.css index 7b4cb75469..7a38fc1164 100755 --- a/filemanager/templates/pixelegg/app.css +++ b/filemanager/templates/pixelegg/app.css @@ -100,20 +100,15 @@ table.egwGridView_grid .tile .file_tile { display: block; max-width: 140px; } -#filemanager-index_nm .tile .file_tile img.vfsMimeIcon { +.tile .file_tile img.vfsMimeIcon { height: auto; width: auto; max-height: 64px; display: block; margin: 0 auto; } -.egwGridView_grid tr.tile:hover .innerContainer { - overflow: visible; -} -.egwGridView_grid tr.tile:hover .file_tile > :not(.iconOverlayContainer) { - position: relative; - z-index: 90; - background-color: white; +.tile .file_tile .et2_label { + word-wrap: break-word; } /** * Select file dialog