You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnosygnu_xowa/150_gfui/xtn/gplx/gfui/Swt_tab_mgr.java

272 lines
13 KiB

/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.gfui;
import gplx.*;
import gplx.core.threads.Thread_adp_;
import org.eclipse.swt.*;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
public class Swt_tab_mgr implements Gxw_tab_mgr, Swt_control, FocusListener, GfoEvMgrOwner {
private GfuiInvkCmd cmd_sync;
// private GfuiInvkCmd cmd_async; // NOTE: async needed for some actions like responding to key_down and calling .setSelection; else app hangs; DATE:2014-04-30
public Swt_tab_mgr(Swt_kit kit, Swt_control owner_control, Keyval_hash ctorArgs) {
this.kit = kit;
tab_folder = new CTabFolder(owner_control.Under_composite(), SWT.BORDER);
tab_folder.setBorderVisible(false);
tab_folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
tab_folder.setSimple(true);
tab_folder.addListener(SWT.Selection, new Swt_tab_mgr_lnr_selection(this));
tab_folder.addKeyListener(new Swt_lnr_key(this));
new Swt_tab_mgr_lnr_drag_drop(this, tab_folder);
tab_folder.addCTabFolder2Listener(new Swt_tab_mgr_lnr_close(this));
core = new Swt_core_cmds(tab_folder);
// cmd_async = kit.New_cmd_async(this);
cmd_sync = kit.New_cmd_sync(this);
}
public Swt_kit Kit() {return kit;} private Swt_kit kit;
public CTabFolder Under_ctabFolder() {return tab_folder;}
@Override public Control Under_control() {return tab_folder;} private CTabFolder tab_folder;
@Override public Composite Under_composite() {return tab_folder;}
@Override public Control Under_menu_control() {return tab_folder;}
public GfoEvMgr EvMgr() {return ev_mgr;} private GfoEvMgr ev_mgr;
public void EvMgr_(GfoEvMgr v) {ev_mgr = v;}
public ColorAdp Btns_selected_color() {return btns_selected_color;} private ColorAdp btns_selected_color;
public void Btns_selected_color_(ColorAdp v) {
btns_selected_color = v;
tab_folder.setSelectionBackground(kit.New_color(v));
}
public ColorAdp Btns_unselected_color() {return btns_unselected_color;}
public void Btns_unselected_color_(ColorAdp v) {
btns_unselected_color = v;
tab_folder.setBackground(kit.New_color(v));
} private ColorAdp btns_unselected_color;
@Override public boolean Btns_curved() {return tab_folder.getSimple();} @Override public void Btns_curved_(boolean v) {tab_folder.setSimple(!v);}
@Override public boolean Btns_place_on_top() {return tab_folder.getTabPosition() == SWT.TOP;}
@Override public void Btns_place_on_top_(boolean v) {tab_folder.setTabPosition(v ? SWT.TOP : SWT.BOTTOM); tab_folder.layout();}
@Override public int Btns_height() {return tab_folder.getTabHeight();} @Override public void Btns_height_(int v) {tab_folder.setTabHeight(v); tab_folder.layout();}
@Override public boolean Btns_close_visible() {return btns_close_visible;} private boolean btns_close_visible = true;
@Override public void Btns_close_visible_(boolean v) {
this.btns_close_visible = v;
CTabItem[] itms = tab_folder.getItems();
int len = itms.length;
for (int i = 0; i < len; i++)
itms[i].setShowClose(v);
}
@Override public boolean Btns_unselected_close_visible() {return tab_folder.getUnselectedCloseVisible();} @Override public void Btns_unselected_close_visible_(boolean v) {
tab_folder.setUnselectedCloseVisible(v);}
@Override public Gxw_tab_itm Tabs_add(Gfui_tab_itm_data tab_data) {
Swt_tab_itm rv = new Swt_tab_itm(this, kit, tab_folder, tab_data);
rv.Under_CTabItem().setData(tab_data);
CTabItem ctab_itm = rv.Under_CTabItem();
ctab_itm.setShowClose(btns_close_visible);
return rv;
}
@Override public void Tabs_close_by_idx(int i) {
CTabItem itm = tab_folder.getItems()[i];
Gfui_tab_itm_data tab_data = Get_tab_data(itm);
CTabItem next_tab = Tabs_select_after_closing_itm(tab_data); // NOTE: must calc next_tab before calling Pub_tab_closed; latter will recalc idx
this.Tabs_select_by_itm(next_tab); // NOTE: select tab before closing; DATE:2014-09-10
Pub_tab_closed(tab_data.Key()); // NOTE: dispose does not call event for .close; must manually raise event;
itm.dispose();
}
@Override public void Tabs_select_by_idx(int i) {
if (i == Gfui_tab_itm_data.Idx_null) return; // 0 tabs; return;
msg_tabs_select_by_idx_swt.Clear();
msg_tabs_select_by_idx_swt.Add("v", i);
cmd_sync.Invk(GfsCtx.Instance, 0, Invk_tabs_select_by_idx_swt, msg_tabs_select_by_idx_swt);
} private GfoMsg msg_tabs_select_by_idx_swt = GfoMsg_.new_cast_(Invk_tabs_select_by_idx_swt);
@Override public void Tabs_switch(int src, int trg) {Tabs_switch(tab_folder.getItem(src), tab_folder.getItem(trg));}
public boolean Tabs_switch(CTabItem src_tab_itm, CTabItem trg_tab_itm) {
Control temp_control = src_tab_itm.getControl();
src_tab_itm.setControl(trg_tab_itm.getControl());
trg_tab_itm.setControl(temp_control);
String temp_str = src_tab_itm.getText();
src_tab_itm.setText(trg_tab_itm.getText());
trg_tab_itm.setText(temp_str);
temp_str = src_tab_itm.getToolTipText();
src_tab_itm.setToolTipText(trg_tab_itm.getToolTipText());
trg_tab_itm.setToolTipText(temp_str);
Gfui_tab_itm_data src_tab_data = Get_tab_data(src_tab_itm);
Gfui_tab_itm_data trg_tab_data = Get_tab_data(trg_tab_itm);
int src_tab_idx = src_tab_data.Idx(), trg_tab_idx = trg_tab_data.Idx();
tab_folder.setSelection(trg_tab_itm);
GfoEvMgr_.PubVals(this, Gfui_tab_mgr.Evt_tab_switched, Keyval_.new_("src", src_tab_data.Key()), Keyval_.new_("trg", trg_tab_data.Key()));
return src_tab_idx < trg_tab_idx;
}
public void Tabs_select_by_itm(CTabItem itm) {
if (itm == null) return; // 0 tabs; return;
msg_tabs_select_by_itm_swt.Clear();
msg_tabs_select_by_itm_swt.Add("v", itm);
cmd_sync.Invk(GfsCtx.Instance, 0, Invk_tabs_select_by_itm_swt, msg_tabs_select_by_itm_swt);
} private GfoMsg msg_tabs_select_by_itm_swt = GfoMsg_.new_cast_(Invk_tabs_select_by_itm_swt);
private void Tabs_select_by_idx_swt(int idx) {
tab_folder.setSelection(idx);
CTabItem itm = tab_folder.getItem(idx);
Pub_tab_selected(Get_tab_key(itm)); // NOTE: setSelection does not call event for SWT.Selection; must manually raise event;
}
private void Tabs_select_by_itm_swt(CTabItem itm) {
tab_folder.setSelection(itm);
Pub_tab_selected(Get_tab_key(itm)); // NOTE: setSelection does not call event for SWT.Selection; must manually raise event;
}
public CTabItem Tabs_select_after_closing_itm(Gfui_tab_itm_data tab_data) {
int next_idx = Gfui_tab_itm_data.Get_idx_after_closing(tab_data.Idx(), tab_folder.getItemCount());
return next_idx == Gfui_tab_itm_data.Idx_null ? null : tab_folder.getItem(next_idx);
}
public void Pub_tab_selected(String key) {
GfoEvMgr_.PubObj(this, Gfui_tab_mgr.Evt_tab_selected, "key", key);
}
public void Pub_tab_closed(String key) {
GfoEvMgr_.PubObj(this, Gfui_tab_mgr.Evt_tab_closed, "key", key);
}
@Override public GxwCore_base Core() {return core;} GxwCore_base core;
@Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host;
@Override public String TextVal() {return "not implemented";}
@Override public void TextVal_set(String v) {}
@Override public void EnableDoubleBuffering() {}
@Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
if (String_.Eq(k, Invk_tabs_select_by_idx_swt)) Tabs_select_by_idx_swt(m.ReadInt("v"));
else if (String_.Eq(k, Invk_tabs_select_by_itm_swt)) Tabs_select_by_itm_swt((CTabItem)m.ReadObj("v", null));
else return GfoInvkAble_.Rv_unhandled;
return this;
}
@Override public void focusGained(FocusEvent arg0) {}
@Override public void focusLost(FocusEvent arg0) {}
private static String
Invk_tabs_select_by_idx_swt = "tabs_select_by_idx"
, Invk_tabs_select_by_itm_swt = "tabs_select_by_itm"
;
public static Gfui_tab_itm_data Get_tab_data_by_obj(Object data) {return (Gfui_tab_itm_data)data;}
public static Gfui_tab_itm_data Get_tab_data(CTabItem itm) {return (Gfui_tab_itm_data)itm.getData();}
public static String Get_tab_key(CTabItem itm) {return ((Gfui_tab_itm_data)itm.getData()).Key();}
}
class Swt_tab_mgr_lnr_selection implements Listener {
public Swt_tab_mgr_lnr_selection(Swt_tab_mgr tab_folder) {this.tab_folder = tab_folder;} private Swt_tab_mgr tab_folder;
public void handleEvent(Event ev) {
tab_folder.Pub_tab_selected(Swt_tab_mgr.Get_tab_data_by_obj(ev.item.getData()).Key());
}
}
class Swt_tab_mgr_lnr_close extends CTabFolder2Adapter { // handles close when tab x is clicked
public Swt_tab_mgr_lnr_close(Swt_tab_mgr tab_folder) {this.tab_folder = tab_folder;} private Swt_tab_mgr tab_folder;
@Override public void close(CTabFolderEvent ev) {
Gfui_tab_itm_data tab_data = Swt_tab_mgr.Get_tab_data_by_obj(ev.item.getData());
tab_folder.Tabs_close_by_idx(tab_data.Idx());
ev.doit = false; // mark ev handled, since Tabs_close_by_idx closes tab
}
}
class Swt_tab_mgr_lnr_drag_drop implements Listener {
private boolean dragging = false;
private boolean drag_stop = false;
private CTabItem drag_itm;
private final Swt_tab_mgr tab_mgr; private final CTabFolder tab_folder; private final Display display;
private Point prv_mouse;
private Point dead_zone = null;
public Swt_tab_mgr_lnr_drag_drop(Swt_tab_mgr tab_mgr, CTabFolder tab_folder) {
this.tab_mgr = tab_mgr; this.tab_folder = tab_folder; this.display = tab_folder.getDisplay();
tab_folder.addListener(SWT.DragDetect, this);
tab_folder.addListener(SWT.MouseUp, this);
tab_folder.addListener(SWT.MouseMove, this);
tab_folder.addListener(SWT.MouseExit, this);
tab_folder.addListener(SWT.MouseEnter, this);
}
private void Drag_drop_bgn(CTabItem itm) {
dragging = true;
drag_stop = false;
drag_itm = itm;
dead_zone = null;
}
private void Drag_drop_end() {
tab_folder.setInsertMark(null, false);
dragging = false;
drag_stop = false;
drag_itm = null;
}
public void handleEvent(Event e) {
Point cur_mouse = e.type == SWT.DragDetect
? tab_folder.toControl(display.getCursorLocation()) //see bug 43251
: new Point(e.x, e.y)
;
switch (e.type) {
case SWT.DragDetect: {
CTabItem itm = tab_folder.getItem(cur_mouse);
if (itm == null) return;
this.Drag_drop_bgn(itm);
break;
}
case SWT.MouseEnter:
if (drag_stop) {
dragging = e.button != 0;
drag_stop = false;
}
break;
case SWT.MouseExit:
if (dragging)
Drag_drop_end();
break;
case SWT.MouseUp: {
if (!dragging) return;
Drag_drop_end();
break;
}
case SWT.MouseMove: {
if (!dragging) return;
CTabItem curr_itm = tab_folder.getItem(cur_mouse);
if (curr_itm == null) {
tab_folder.setInsertMark(null, false);
return;
}
if (curr_itm == drag_itm) return; // curr_itm is same as drag_itm; ignore
int cur_mouse_x = cur_mouse.x;
int prv_mouse_x = prv_mouse == null ? 0 : prv_mouse.x;
prv_mouse = cur_mouse; // set prv_mouse now b/c of early return below; note that cur_mouse_x and prv_mouse_x are cached above
if ( dead_zone != null // dead_zone exists
&& Int_.Between(cur_mouse_x, dead_zone.x, dead_zone.y)) { // mouse is in dead_zone
int drag_idx = Swt_tab_mgr.Get_tab_data(drag_itm).Idx();
int curr_idx = Swt_tab_mgr.Get_tab_data(curr_itm).Idx();
if (drag_idx > curr_idx && cur_mouse_x < prv_mouse_x) {} // drag_itm is right of curr_itm, but mouse is moving left (direction reversed); cancel
else if (drag_idx < curr_idx && cur_mouse_x > prv_mouse_x) {} // drag_itm is left of curr_itm, but mouse is moving right (direction reversed); cancel
else
return; // in dead zone, and still moving in original direction; return early
}
boolean fwd = tab_mgr.Tabs_switch(drag_itm, curr_itm);
drag_itm = curr_itm;
Rectangle drag_rect = drag_itm.getBounds();
dead_zone = Calc_dead_zone(fwd, cur_mouse_x, drag_rect.x, drag_rect.width);
break;
}
}
}
public static Point Calc_dead_zone(boolean fwd, int mouse_x, int drag_l, int drag_w) {
if (fwd) { // drag_itm was moving fwd (moving right)
if (mouse_x < drag_l) return new Point(mouse_x, drag_l); // mouse_x < drag_l; create dead_zone until mouse_x reaches drag_l; occurs when moving drag is small_title and trg_itm is large_title
}
else { // drag_itm was moving bwd (moving left)
int drag_r = drag_l + drag_w;
if (mouse_x > drag_r) return new Point(drag_r, mouse_x); // mouse_x > drag_r; create dead_zone until mouse_x reaches drag_r
}
return null;
}
}
//#}