summaryrefslogtreecommitdiff
path: root/program
diff options
context:
space:
mode:
authoralecpl <alec@alec.pl>2010-05-02 15:09:36 +0000
committeralecpl <alec@alec.pl>2010-05-02 15:09:36 +0000
commitb62c4869f7c43804601b7786809e7e7a2508566d (patch)
treea2912d83818b22b0f64ed9cb8e466e386a8e7d26 /program
parentd44571bde1db578e69d82ad0121e9b44a89991a0 (diff)
- Allow columns order change per user - drag&drop (#1485795)
Diffstat (limited to 'program')
-rw-r--r--program/js/app.js54
-rw-r--r--program/js/list.js269
-rw-r--r--program/steps/mail/func.inc23
-rw-r--r--program/steps/mail/list.inc2
4 files changed, 310 insertions, 38 deletions
diff --git a/program/js/app.js b/program/js/app.js
index b2888d028..dd3feeeca 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -166,7 +166,8 @@ function rcube_webmail()
if (this.gui_objects.messagelist) {
this.message_list = new rcube_list_widget(this.gui_objects.messagelist,
- {multiselect:true, multiexpand:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time});
+ {multiselect:true, multiexpand:true, draggable:true, keyboard:true,
+ column_movable:this.env.col_movable, column_fixed:0, dblclick_time:this.dblclick_time});
this.message_list.row_init = function(o){ p.init_message_row(o); };
this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); });
this.message_list.addEventListener('click', function(o){ p.msglist_click(o); });
@@ -176,11 +177,11 @@ function rcube_webmail()
this.message_list.addEventListener('dragmove', function(e){ p.drag_move(e); });
this.message_list.addEventListener('dragend', function(e){ p.drag_end(e); });
this.message_list.addEventListener('expandcollapse', function(e){ p.msglist_expand(e); });
+ this.message_list.addEventListener('column_replace', function(e){ p.msglist_set_coltypes(e); });
document.onmouseup = function(e){ return p.doc_mouse_up(e); };
this.gui_objects.messagelist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
- this.set_message_coltypes(this.env.coltypes);
this.message_list.init();
this.enable_command('toggle_status', 'toggle_flag', 'menu-open', 'menu-save', true);
@@ -1487,6 +1488,24 @@ function rcube_webmail()
if (this.env.messages[row.uid])
this.env.messages[row.uid].expanded = row.expanded;
};
+
+ this.msglist_set_coltypes = function(list)
+ {
+ var i, found, name, cols = list.list.tHead.rows[0].cells;
+
+ this.env.coltypes = [];
+
+ for (i=0; i<cols.length; i++)
+ if (cols[i].id && cols[i].id.match(/^rcm/)) {
+ name = cols[i].id.replace(/^rcm/, '');
+ this.env.coltypes[this.env.coltypes.length] = name == 'to' ? 'from' : name;
+ }
+
+ if ((found = $.inArray('flag', this.env.coltypes)) >= 0)
+ this.set_env('flagged_col', found);
+
+ this.http_post('save-pref', { '_name':'list_cols', '_value':this.env.coltypes });
+ };
this.check_droptarget = function(id)
{
@@ -1692,6 +1711,9 @@ function rcube_webmail()
{
var update, add_url = '';
+ if (!sort_col) sort_col = this.env.sort_col;
+ if (!sort_order) sort_order = this.env.sort_order;
+
if (this.env.sort_col != sort_col || this.env.sort_order != sort_order) {
update = 1;
this.set_list_sorting(sort_col, sort_order);
@@ -1702,9 +1724,25 @@ function rcube_webmail()
add_url += '&_threads=' + threads;
}
- if (cols.join() != this.env.coltypes.join()) {
- update = 1;
- add_url += '&_cols=' + cols.join(',');
+ if (cols && cols.length) {
+ // make sure new columns are added at the end of the list
+ var i, idx, name, newcols = [], oldcols = this.env.coltypes;
+ for (i=0; i<oldcols.length; i++) {
+ name = oldcols[i] == 'to' ? 'from' : oldcols[i];
+ idx = $.inArray(name, cols);
+ if (idx != -1) {
+ newcols[newcols.length] = name;
+ delete cols[idx];
+ }
+ }
+ for (i=0; i<cols.length; i++)
+ if (cols[i])
+ newcols[newcols.length] = cols[i];
+
+ if (newcols.join() != this.env.coltypes.join()) {
+ update = 1;
+ add_url += '&_cols=' + newcols.join(',');
+ }
}
if (update)
@@ -4439,13 +4477,15 @@ function rcube_webmail()
this.env.flagged_col = null;
var found;
- if((found = $.inArray('subject', this.env.coltypes)) >= 0) {
+ if ((found = $.inArray('subject', this.env.coltypes)) >= 0) {
this.set_env('subject_col', found);
if (this.message_list)
this.message_list.subject_col = found+1;
}
- if((found = $.inArray('flag', this.env.coltypes)) >= 0)
+ if ((found = $.inArray('flag', this.env.coltypes)) >= 0)
this.set_env('flagged_col', found);
+
+ this.message_list.init_header();
};
// replace content of row count display
diff --git a/program/js/list.js b/program/js/list.js
index 8d6d9f1e6..9493121d5 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -33,6 +33,7 @@ function rcube_list_widget(list, p)
this.rows = [];
this.selection = [];
this.rowcount = 0;
+ this.colcount = 0;
this.subject_col = -1;
this.shiftkey = false;
@@ -40,11 +41,14 @@ function rcube_list_widget(list, p)
this.multiexpand = false;
this.multi_selecting = false;
this.draggable = false;
+ this.column_movable = false;
this.keyboard = false;
this.toggleselect = false;
this.dont_select = false;
this.drag_active = false;
+ this.col_drag_active = false;
+ this.column_fixed = null;
this.last_selected = 0;
this.shift_start = 0;
this.in_selection_before = false;
@@ -72,18 +76,15 @@ init: function()
this.rows = [];
this.rowcount = 0;
- var row;
- for(var r=0; r<this.list.tBodies[0].childNodes.length; r++) {
- row = this.list.tBodies[0].childNodes[r];
- while (row && row.nodeType != 1) {
- row = row.nextSibling;
- r++;
- }
+ var row, r;
+ for (r=0; r<this.list.tBodies[0].rows.length; r++) {
+ row = this.list.tBodies[0].rows[r];
this.init_row(row);
this.rowcount++;
}
+ this.init_header();
this.frame = this.list.parentNode;
// set body events
@@ -120,6 +121,29 @@ init_row: function(row)
/**
+ * Init list column headers and set mouse events on them
+ */
+init_header: function()
+{
+ if (this.list && this.list.tHead) {
+ this.colcount = 0;
+
+ var col, r, p = this;
+ // add events for list columns moving
+ if (this.column_movable && this.list.tHead && this.list.tHead.rows) {
+ for (r=0; r<this.list.tHead.rows[0].cells.length; r++) {
+ if (this.column_fixed == r)
+ continue;
+ col = this.list.tHead.rows[0].cells[r];
+ col.onmousedown = function(e){ return p.drag_column(e, this); };
+ this.colcount++;
+ }
+ }
+ }
+},
+
+
+/**
* Remove all list rows
*/
clear: function(sel)
@@ -207,6 +231,34 @@ blur: function()
/**
+ * onmousedown-handler of message list column
+ */
+drag_column: function(e, col)
+{
+ if (this.colcount > 1) {
+ this.drag_start = true;
+ this.drag_mouse_start = rcube_event.get_mouse_pos(e);
+
+ rcube_event.add_listener({event:'mousemove', object:this, method:'column_drag_mouse_move'});
+ rcube_event.add_listener({event:'mouseup', object:this, method:'column_drag_mouse_up'});
+
+ // enable dragging over iframes
+ this.add_dragfix();
+
+ // find selected column number
+ for (var i=0; i<this.list.tHead.rows[0].cells.length; i++) {
+ if (col == this.list.tHead.rows[0].cells[i]) {
+ this.selected_column = i;
+ break;
+ }
+ }
+ }
+
+ return false;
+},
+
+
+/**
* onmousedown-handler of message list row
*/
drag_row: function(e, id)
@@ -236,15 +288,7 @@ drag_row: function(e, id)
rcube_event.add_listener({event:'mouseup', object:this, method:'drag_mouse_up'});
// enable dragging over iframes
- $('iframe').each(function() {
- $('<div class="iframe-dragdrop-fix"></div>')
- .css({background: '#fff',
- width: this.offsetWidth+'px', height: this.offsetHeight+'px',
- position: 'absolute', opacity: '0.001', zIndex: 1000
- })
- .css($(this).offset())
- .appendTo('body');
- });
+ this.add_dragfix();
}
return false;
@@ -287,7 +331,7 @@ click_row: function(e, id)
if (!this.drag_active) {
// remove temp divs
- $('div.iframe-dragdrop-fix').each(function() { this.parentNode.removeChild(this); });
+ this.del_dragfix();
rcube_event.cancel(e);
}
@@ -931,7 +975,11 @@ key_down: function(e)
switch (rcube_event.get_keycode(e)) {
case 27:
if (this.drag_active)
- return this.drag_mouse_up(e);
+ return this.drag_mouse_up(e);
+ if (this.col_drag_active) {
+ this.selected_column = null;
+ return this.column_drag_mouse_up(e);
+ }
case 40:
case 38:
@@ -1040,7 +1088,9 @@ drag_mouse_move: function(e)
return false;
if (!this.draglayer)
- this.draglayer = $('<div>').attr('id', 'rcmdraglayer').css({ position:'absolute', display:'none', 'z-index':2000 }).appendTo(document.body);
+ this.draglayer = $('<div>').attr('id', 'rcmdraglayer')
+ .css({ position:'absolute', display:'none', 'z-index':2000 })
+ .appendTo(document.body);
// also select childs of (collapsed) threads for dragging
var selection = $.merge([], this.selection);
@@ -1134,7 +1184,7 @@ drag_mouse_up: function(e)
rcube_event.remove_listener({event:'mouseup', object:this, method:'drag_mouse_up'});
// remove temp divs
- $('div.iframe-dragdrop-fix').each(function() { this.parentNode.removeChild(this); });
+ this.del_dragfix();
this.triggerEvent('dragend');
@@ -1143,6 +1193,185 @@ drag_mouse_up: function(e)
/**
+ * Handler for mouse move events for dragging list column
+ */
+column_drag_mouse_move: function(e)
+{
+ if (this.drag_start) {
+ // check mouse movement, of less than 3 pixels, don't start dragging
+ var i, m = rcube_event.get_mouse_pos(e);
+
+ if (!this.drag_mouse_start || (Math.abs(m.x - this.drag_mouse_start.x) < 3 && Math.abs(m.y - this.drag_mouse_start.y) < 3))
+ return false;
+
+ if (!this.col_draglayer) {
+ var lpos = $(this.list).offset(),
+ cells = this.list.tHead.rows[0].cells;
+
+ // create dragging layer
+ this.col_draglayer = $('<div>').attr('id', 'rcmcoldraglayer')
+ .css(lpos).css({ position:'absolute', 'z-index':2001,
+ 'background-color':'white', opacity:0.75,
+ height: (this.frame.offsetHeight-2)+'px', width: (this.frame.offsetWidth-2)+'px' })
+ .appendTo(document.body)
+ // ... and column position indicator
+ .append($('<div>').attr('id', 'rcmcolumnindicator')
+ .css({ position:'absolute', 'border-right':'2px dotted #555',
+ 'z-index':2002, height: (this.frame.offsetHeight-2)+'px' }));
+
+ this.cols = [];
+ this.list_pos = this.list_min_pos = lpos.left;
+ // save columns positions
+ for (i=0; i<cells.length; i++) {
+ this.cols[i] = cells[i].offsetWidth;
+ if (this.column_fixed !== null && i <= this.column_fixed) {
+ this.list_min_pos += this.cols[i];
+ }
+ }
+ }
+
+ this.col_draglayer.show();
+ this.col_drag_active = true;
+ this.triggerEvent('column_dragstart');
+ }
+
+ // set column indicator position
+ if (this.col_drag_active && this.col_draglayer) {
+ var i, cpos = 0, pos = rcube_event.get_mouse_pos(e);
+
+ for (i=0; i<this.cols.length; i++) {
+ if (pos.x >= this.cols[i]/2 + this.list_pos + cpos)
+ cpos += this.cols[i];
+ else
+ break;
+ }
+
+ // handle fixed columns on left
+ if (i == 0 && this.list_min_pos > pos.x)
+ cpos = this.list_min_pos - this.list_pos;
+ // empty list needs some assignment
+ else if (!this.list.rowcount && i == this.cols.length)
+ cpos -= 2;
+ $('#rcmcolumnindicator').css({ width: cpos+'px'});
+ this.triggerEvent('column_dragmove', e?e:window.event);
+ }
+
+ this.drag_start = false;
+
+ return false;
+},
+
+
+/**
+ * Handler for mouse up events for dragging list columns
+ */
+column_drag_mouse_up: function(e)
+{
+ document.onmousemove = null;
+
+ if (this.col_draglayer) {
+ (this.col_draglayer).remove();
+ this.col_draglayer = null;
+ }
+
+ if (this.col_drag_active)
+ this.focus();
+ this.col_drag_active = false;
+
+ rcube_event.remove_listener({event:'mousemove', object:this, method:'column_drag_mouse_move'});
+ rcube_event.remove_listener({event:'mouseup', object:this, method:'column_drag_mouse_up'});
+ // remove temp divs
+ this.del_dragfix();
+
+ if (this.selected_column !== null && this.cols && this.cols.length) {
+ var i, cpos = 0, pos = rcube_event.get_mouse_pos(e);
+
+ // find destination position
+ for (i=0; i<this.cols.length; i++) {
+ if (pos.x >= this.cols[i]/2 + this.list_pos + cpos)
+ cpos += this.cols[i];
+ else
+ break;
+ }
+
+ if (i != this.selected_column && i != this.selected_column+1) {
+ this.column_replace(this.selected_column, i);
+ }
+ }
+
+ this.triggerEvent('column_dragend');
+
+ return rcube_event.cancel(e);
+},
+
+
+/**
+ * Creates a layer for drag&drop over iframes
+ */
+add_dragfix: function()
+{
+ $('iframe').each(function() {
+ $('<div class="iframe-dragdrop-fix"></div>')
+ .css({background: '#fff',
+ width: this.offsetWidth+'px', height: this.offsetHeight+'px',
+ position: 'absolute', opacity: '0.001', zIndex: 1000
+ })
+ .css($(this).offset())
+ .appendTo(document.body);
+ });
+},
+
+
+/**
+ * Removes the layer for drag&drop over iframes
+ */
+del_dragfix: function()
+{
+ $('div.iframe-dragdrop-fix').each(function() { this.parentNode.removeChild(this); });
+},
+
+
+/**
+ * Replaces two columns
+ */
+column_replace: function(from, to)
+{
+ var cells = this.list.tHead.rows[0].cells,
+ elem = cells[from],
+ before = cells[to],
+ td = document.createElement('td');
+
+ // replace header cells
+ if (before)
+ cells[0].parentNode.insertBefore(td, before);
+ else
+ cells[0].parentNode.appendChild(td);
+ cells[0].parentNode.replaceChild(elem, td);
+
+ // replace list cells
+ for (r=0; r<this.list.tBodies[0].rows.length; r++) {
+ row = this.list.tBodies[0].rows[r];
+
+ elem = row.cells[from];
+ before = row.cells[to];
+ td = document.createElement('td');
+
+ if (before)
+ row.insertBefore(td, before);
+ else
+ row.appendChild(td);
+ row.replaceChild(elem, td);
+ }
+
+ // update subject column position
+ if (this.subject_col == from)
+ this.subject_col = to > from ? to - 1 : to;
+
+ this.triggerEvent('column_replace');
+},
+
+
+/**
* Creating the list in background
*/
set_background_mode: function(flag)
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index bd7fa9c43..6a56d2a75 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -154,13 +154,16 @@ function rcmail_message_list($attrib)
$attrib['id'] = 'rcubemessagelist';
// define list of cols to be displayed based on parameter or config
- if (empty($attrib['columns']))
- $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
- else
- $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
+ if (empty($attrib['columns'])) {
+ $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
+ $OUTPUT->set_env('col_movable', !in_array('list_cols', (array)$CONFIG['dont_override']));
+ }
+ else {
+ $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns']));
+ $attrib['columns'] = $a_show_cols;
+ }
// save some variables for use in ajax list
- $_SESSION['list_columns'] = $a_show_cols;
$_SESSION['list_attrib'] = $attrib;
$mbox = $IMAP->get_mailbox_name();
@@ -220,7 +223,7 @@ function rcmail_message_list($attrib)
$attrib,
html::tag('thead', null, html::tag('tr', null, $thead)) .
html::tag('tbody', null, ''),
- array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+ array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
}
@@ -232,10 +235,10 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $replace=TRUE, $h
{
global $CONFIG, $IMAP, $OUTPUT;
- if (empty($_SESSION['list_columns']))
- $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
+ if (!empty($_SESSION['list_attrib']['columns']))
+ $a_show_cols = $_SESSION['list_attrib']['columns'];
else
- $a_show_cols = $_SESSION['list_columns'];
+ $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
$mbox = $IMAP->get_mailbox_name();
$delim = $IMAP->get_hierarchy_delimiter();
@@ -324,7 +327,7 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $replace=TRUE, $h
if ($browser->ie && $replace)
$OUTPUT->command('offline_message_list', false);
- }
+}
/*
diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc
index 89d127cdc..3b8c39fc8 100644
--- a/program/steps/mail/list.inc
+++ b/program/steps/mail/list.inc
@@ -45,7 +45,7 @@ else
if ($cols = get_input_value('_cols', RCUBE_INPUT_GET))
{
$save_arr = array();
- $_SESSION['list_columns'] = $save_arr['list_cols'] = explode(',', $cols);
+ $save_arr['list_cols'] = explode(',', $cols);
}
if ($save_arr)