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