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.

1510 lines
40 KiB

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <libgen.h>
#include <Eina.h>
#include <Eio.h>
#include <Elementary.h>
#include "mainview/edi_mainview_item.h"
#include "mainview/edi_mainview_panel.h"
#include "mainview/edi_mainview.h"
#include "edi_filepanel.h"
#include "editor/edi_editor.h"
#include "edi_content_provider.h"
#include "edi_private.h"
#include "edi_config.h"
typedef struct _Edi_Mainview_State {
Edi_Mainview_Panel *panel;
char *path;
} Edi_Mainview_State;
static Edi_Mainview_State *_cached = NULL;
static Evas_Object *_main_win;
static Evas_Object *_edi_mainview_goto_popup;
static void
dummy()
{}
static void
_edi_mainview_panel_cache_set(void)
{
Edi_Mainview_Item *item;
if (!_cached)
_cached = calloc(1, sizeof(Edi_Mainview_State));
if (_cached->path)
{
free(_cached->path);
_cached->path = NULL;
}
_cached->panel = edi_mainview_panel_current_get();
item = edi_mainview_item_current_get();
if (item)
_cached->path = strdup(item->path);
}
static Edi_Mainview_State *
_edi_mainview_panel_cache_get(void)
{
if (!_cached || !_cached->panel || !_cached->path)
return NULL;
return _cached;
}
static Edi_Mainview_Item *
_get_item_for_path(Edi_Mainview_Panel *panel, const char *path)
{
Eina_List *item;
Edi_Mainview_Item *it;
if (!panel || !path) return NULL;
EINA_LIST_FOREACH(panel->items, item, it)
{
if (it && !strcmp(it->path, path))
return it;
}
return NULL;
}
unsigned int
edi_mainview_panel_item_count(Edi_Mainview_Panel *panel)
{
return eina_list_count(panel->items);
}
Edi_Mainview_Item *
edi_mainview_panel_item_current_get(Edi_Mainview_Panel *panel)
{
if (!panel) return NULL;
return panel->current;
}
unsigned int
edi_mainview_panel_item_current_tab_get(Edi_Mainview_Panel *panel)
{
Eina_List *item;
Edi_Mainview_Item *it;
unsigned int i = 0;
EINA_LIST_FOREACH(panel->items, item, it)
{
if (!it->win)
i++;
if (it && it == panel->current)
break;
}
return i;
}
static void
_edi_mainview_panel_current_tab_hide(Edi_Mainview_Panel *panel)
{
Edi_Editor *editor;
edi_mainview_panel_focus(panel);
if (!panel || !panel->current)
return;
editor = (Edi_Editor *)evas_object_data_get(panel->current->view, "editor");
if (editor)
{
elm_object_focus_set(editor->entry, EINA_FALSE);
evas_object_hide(editor->entry);
}
}
static void
_edi_mainview_panel_current_tab_show(Edi_Mainview_Panel *panel)
{
Edi_Editor *editor;
edi_mainview_panel_focus(panel);
if (!panel || !panel->current)
return;
editor = (Edi_Editor *)evas_object_data_get(panel->current->view, "editor");
if (editor)
{
evas_object_show(editor->entry);
elm_object_focus_set(editor->entry, EINA_TRUE);
}
}
Edi_Mainview_Item *
_edi_mainview_panel_item_for_view_get(Edi_Mainview_Panel *panel, Evas_Object *view)
{
Eina_List *item;
Edi_Mainview_Item *it;
EINA_LIST_FOREACH(panel->items, item, it)
{
if (it->view == view)
return it;
}
return NULL;
}
Eina_Bool
edi_mainview_panel_item_contains(Edi_Mainview_Panel *panel, Edi_Mainview_Item *item)
{
Eina_List *it;
Edi_Mainview_Item *panel_item;
EINA_LIST_FOREACH(panel->items, it, panel_item)
{
if (panel_item == item)
return EINA_TRUE;
}
return EINA_FALSE;
}
void
edi_mainview_panel_item_prev(Edi_Mainview_Panel *panel)
{
Eina_List *item;
Edi_Mainview_Item *it, *first, *prev = NULL;
first = (Edi_Mainview_Item *)eina_list_nth(panel->items, 0);
if (first == panel->current)
{
prev = eina_list_nth(panel->items, eina_list_count(panel->items)-1);
if (prev)
edi_mainview_panel_item_select(panel, prev);
return;
}
EINA_LIST_FOREACH(panel->items, item, it)
{
if (it && it == panel->current)
{
if (prev)
edi_mainview_panel_item_select(panel, prev);
return;
}
prev = it;
}
}
void
edi_mainview_panel_item_next(Edi_Mainview_Panel *panel)
{
Eina_List *item;
Edi_Mainview_Item *it, *last, *next;
Eina_Bool open_next = EINA_FALSE;
last = eina_list_nth(panel->items, eina_list_count(panel->items)-1);
if (last == panel->current)
{
next = eina_list_nth(panel->items, 0);
if (next)
edi_mainview_panel_item_select(panel, next);
return;
}
EINA_LIST_FOREACH(panel->items, item, it)
{
if (it && open_next)
{
edi_mainview_panel_item_select(panel, it);
return;
}
if (it && it == panel->current)
open_next = EINA_TRUE;
}
}
void
edi_mainview_panel_tab_select(Edi_Mainview_Panel *panel, unsigned int id)
{
Eina_List *item;
Edi_Mainview_Item *it;
unsigned int i = 0;
EINA_LIST_FOREACH(panel->items, item, it)
{
if (!it->win)
i++;
if (i == id)
edi_mainview_panel_item_select(panel, it);
}
}
static void
_edi_mainview_panel_show(Edi_Mainview_Panel *panel, Evas_Object *view)
{
if (panel->current)
{
elm_box_unpack(panel->content, panel->current->view);
evas_object_hide(panel->current->view);
}
else
{
elm_box_unpack(panel->content, panel->welcome);
evas_object_hide(panel->welcome);
}
panel->current = _edi_mainview_panel_item_for_view_get(panel, view);
elm_box_pack_end(panel->content, view);
evas_object_show(view);
}
static void
_content_load(Edi_Mainview_Item *item)
{
Edi_Content_Provider *provider;
Evas_Object *child;
provider = edi_content_provider_for_id_get(item->editortype);
if (!provider)
{
ERR("No content provider found for type %s", item->editortype);
return;
}
child = provider->content_ui_add(item->container, item);
evas_object_size_hint_weight_set(child, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(child, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_pack_end(item->container, child);
evas_object_show(child);
item->loaded = EINA_TRUE;
}
void
edi_mainview_panel_item_close(Edi_Mainview_Panel *panel, Edi_Mainview_Item *item)
{
int item_index;
Eina_Bool current;
if (!item)
return;
current = (item == panel->current);
item_index = eina_list_data_idx(panel->items, item);
if (item->view)
evas_object_del(item->view);
elm_box_unpack(panel->tabs, item->tab->button);
evas_object_del(item->tab->button);
panel->items = eina_list_remove(panel->items, item);
_edi_project_config_tab_remove(item->path, EINA_FALSE,
edi_mainview_panel_index_get(panel));
eina_stringshare_del(item->path);
if (item->tab->path)
free(item->tab->path);
free(item->tab);
free(item);
if (!current)
return;
if (eina_list_count(panel->items) == 0)
{
_edi_mainview_panel_show(panel, panel->welcome);
return;
}
if (item_index)
item = eina_list_nth(panel->items, item_index - 1);
else
item = eina_list_nth(panel->items, item_index);
edi_mainview_panel_item_select(panel, item);
_edi_mainview_panel_current_tab_show(panel);
}
void
edi_mainview_panel_item_select(Edi_Mainview_Panel *panel, Edi_Mainview_Item *item)
{
Eina_List *list;
Edi_Mainview_Item *it;
Evas_Coord tabw, region_x = 0, w, total_w = 0;
if (item->win)
{
elm_win_raise(item->win);
}
else
{
EINA_LIST_FOREACH(panel->items, list, it)
{
elm_object_signal_emit(it->tab->button, "mouse,up,1", "base");
evas_object_geometry_get(it->tab->button, NULL, NULL, &w, NULL);
if (item == it) region_x = total_w;
total_w += w;
}
if (!item->loaded)
_content_load(item);
_edi_mainview_panel_show(panel, item->view);
elm_object_signal_emit(item->tab->button, "mouse,down,1", "base");
evas_object_geometry_get(item->tab->button, NULL, NULL, &tabw, NULL);
elm_scroller_region_bring_in(efl_parent_get(panel->tabs), region_x, 0, tabw, 0);
_edi_project_config_tab_current_set(edi_mainview_panel_index_get(panel),
edi_mainview_panel_item_current_tab_get(panel));
_edi_project_config_save_no_notify();
}
edi_mainview_panel_focus(panel);
ecore_event_add(EDI_EVENT_TAB_CHANGED, NULL, NULL, NULL);
}
static void
_promote(void *data, Evas_Object *obj EINA_UNUSED,
const char *emission EINA_UNUSED, const char *source)
{
Edi_Mainview_Panel *panel;
Edi_Mainview_Item *item = (Edi_Mainview_Item *) data;
// ignore if we clicked the delete part of the button
if (!strcmp(source, "del"))
return;
panel = edi_mainview_panel_for_item_get(item);
_edi_mainview_panel_cache_set();
_edi_mainview_panel_current_tab_hide(panel);
edi_mainview_panel_item_select(panel, item);
_edi_mainview_panel_current_tab_show(panel);
edi_filepanel_select_path(item->path);
}
static void
_closetab(void *data, Evas_Object *obj EINA_UNUSED,
const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
{
Edi_Mainview_Panel *panel;
Edi_Mainview_Item *item;
Edi_Mainview_State *last;
Edi_Editor *editor;
int index;
item = (Edi_Mainview_Item *) data;
panel = edi_mainview_panel_for_item_get(item);
editor = (Edi_Editor *) evas_object_data_get(panel->current->view, "editor");
if (editor && eina_list_count(editor->split_views))
{
Elm_Code *code;
const char *path;
Elm_Code_Widget *widget = eina_list_nth(editor->split_views, 0);
elm_box_unpack(panel->current->container, widget);
code = elm_code_widget_code_get(editor->entry);
path = elm_code_file_path_get(code->file);
editor->split_views = eina_list_remove(editor->split_views, widget);
_edi_project_config_tab_split_view_count_set(path, edi_mainview_panel_id(panel), eina_list_count(editor->split_views));
evas_object_del(widget);
return;
}
edi_mainview_panel_item_close(panel, item);
if (eina_list_count(panel->items)== 0 && edi_mainview_panel_count() > 1)
{
edi_mainview_panel_remove(panel);
index = edi_mainview_panel_count() - 1;
panel = edi_mainview_panel_by_index(index);
}
edi_mainview_panel_focus(panel);
/* When closing tabs keep current tab */
last = _edi_mainview_panel_cache_get();
if (last && last->panel == panel && last->path)
{
item = _get_item_for_path(panel, last->path);
if (item)
{
_edi_mainview_panel_current_tab_hide(panel);
edi_mainview_panel_item_select(panel, item);
}
}
if (eina_list_count(panel->items))
_edi_mainview_panel_current_tab_show(panel);
if (eina_list_count(panel->items) == 0 && edi_mainview_panel_count() == 1)
{
edi_main_win_title_reset();
}
}
static Evas_Object *
_edi_mainview_panel_content_create(Edi_Mainview_Item *item, Evas_Object *parent)
{
Evas_Object *container;
container = elm_box_add(parent);
evas_object_size_hint_weight_set(container, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(container, EVAS_HINT_FILL, EVAS_HINT_FILL);
item->loaded = EINA_FALSE;
item->container = container;
return container;
}
static int
_font_width_get(Evas_Object *parent, const char *text)
{
int w = 0;
Evas_Object *textblock = evas_object_text_add(parent);
evas_object_size_hint_weight_set(textblock, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(textblock, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_show(textblock);
evas_object_text_font_set(textblock, "sans", 12);
evas_object_text_text_set(textblock, text);
evas_object_geometry_get(textblock, NULL, NULL, &w, NULL);
evas_object_del(textblock);
if (w < 120)
w = 120;
return w;
}
// TODO: Allow tab moving between panels.
static char *_tab_move_src_path = NULL;
static void
_tab_swap(const char *path1, const char *path2)
{
Edi_Mainview_Panel *panel, *p1, *p2;
Edi_Mainview_Item *item, *it1, *it2;
Edi_Mainview_Item *tmp1, *tmp2;
Eina_List *l, *l_next;
it1 = it2 = NULL;
if (!strcmp(path1, path2)) return;
int count = edi_mainview_panel_count();
for (int i = 0; i < count; i++)
{
panel = edi_mainview_panel_by_index(i);
EINA_LIST_FOREACH(panel->items, l, item)
{
if (!strcmp(item->path, path1))
{
it1 = item;
p1 = panel;
}
else if (!strcmp(item->path, path2))
{
it2 = item;
p2 = panel;
}
if (it1 && it2) break;
}
}
if ((!it1 || !it2) || (p1 != p2)) return;
tmp1 = it1;
tmp2 = it2;
panel = p1;
EINA_LIST_FOREACH_SAFE(panel->items, l, l_next, item)
{
if (item == it1)
l = eina_list_data_set(l, tmp2);
if (item == it2)
l = eina_list_data_set(l, tmp1);
}
elm_box_unpack_all(panel->tabs);
EINA_LIST_FOREACH(panel->items, l, item)
{
elm_box_pack_end(panel->tabs, item->tab->button);
}
elm_box_recalculate(panel->tabs);
edi_mainview_panel_item_select_path(panel, path2);
}
static void
_tab_move_display_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Evas_Event_Mouse_Move *ev;
Edi_Mainview_Item_Tab *tab = data;
if (!tab) return;
ev = event_info;
elm_object_tooltip_hide(tab->button);
evas_object_move(tab->button_drag, ev->cur.canvas.x, ev->cur.canvas.y);
evas_object_show(tab->button_drag);
}
static void
_tab_move_done_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Edi_Mainview_Item_Tab *tab = data;
if (!tab) return;
evas_object_event_callback_del(tab->button, EVAS_CALLBACK_MOUSE_UP, _tab_move_done_cb);
evas_object_event_callback_del(tab->toolbar, EVAS_CALLBACK_MOUSE_MOVE, _tab_move_display_cb);
evas_object_del(tab->button_drag);
if (_tab_move_src_path)
{
free(_tab_move_src_path);
_tab_move_src_path = NULL;
}
}
static void
_tab_move_begin_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Evas_Event_Mouse_Down *ev;
Evas_Object *btn;
Edi_Mainview_Item_Tab *tab;
Evas_Coord w, h;
tab = data;
if (!tab || _tab_move_src_path) return;
ev = event_info;
/* Duplicate our tab visually in a box we can drag. */
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
tab->button_drag = elm_box_add(edi_main_win_get());
btn = elm_button_add(tab->button_drag);
elm_box_pack_end(tab->button_drag, btn);
elm_layout_theme_set(btn, "multibuttonentry", "btn", "default");
evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(btn, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_part_text_set(btn, "elm.btn.text", elm_object_part_text_get(obj, "elm.btn.text"));
elm_object_focus_set(btn, EINA_TRUE);
evas_object_show(btn);
evas_object_move(tab->button_drag, ev->output.x, ev->output.y);
evas_object_resize(tab->button_drag, w, h);
_tab_move_src_path = strdup(tab->path);
evas_object_event_callback_add(tab->toolbar, EVAS_CALLBACK_MOUSE_MOVE, _tab_move_display_cb, tab);
evas_object_event_callback_add(tab->button, EVAS_CALLBACK_MOUSE_UP, _tab_move_done_cb, tab);
}
static void
_tab_mouse_in_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Edi_Mainview_Item_Tab *tab = data;
if (!tab || !tab->path || !_tab_move_src_path) return;
_tab_swap(tab->path, _tab_move_src_path);
}
static void
_edi_mainview_panel_item_tab_add(Edi_Mainview_Panel *panel, Edi_Path_Options *options, const char *mime)
{
Evas_Object *content, *btn;
Edi_Mainview_Item *item;
Edi_Editor *editor;
Elm_Code *code;
int h, width;
const char *path;
if (!panel) return;
int id = edi_mainview_panel_id(panel);
if (panel == edi_mainview_panel_current_get())
{
if (eina_list_count(panel->items))
_edi_mainview_panel_current_tab_hide(panel);
}
item = edi_mainview_item_add(options, mime, NULL, NULL);
content = _edi_mainview_panel_content_create(item, panel->content);
item->view = content;
panel->items = eina_list_append(panel->items, item);
_edi_mainview_panel_show(panel, content);
evas_object_geometry_get(panel->tabs, NULL, NULL, NULL, &h);
btn = elm_button_add(panel->tabs);
evas_object_size_hint_weight_set(btn, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(btn, 0.0, EVAS_HINT_FILL);
elm_object_focus_allow_set(btn, EINA_FALSE);
path = strstr(item->path, edi_project_get());
if (path)
path += 1 + strlen(edi_project_get());
else
path = item->path;
elm_object_tooltip_text_set(btn, path);
elm_object_tooltip_window_mode_set(btn, EINA_TRUE);
elm_layout_theme_set(btn, "multibuttonentry", "btn", "default");
elm_object_part_text_set(btn, "elm.btn.text", eina_slstr_printf("<style align=left> %s</>", ecore_file_file_get(options->path)));
item->tab->toolbar = panel->tabs;
item->tab->button = btn;
item->tab->path = strdup(item->path);
evas_object_event_callback_add(btn, EVAS_CALLBACK_MOUSE_DOWN, _tab_move_begin_cb, item->tab);
evas_object_event_callback_add(btn, EVAS_CALLBACK_MOUSE_IN, _tab_mouse_in_cb, item->tab);
width = _font_width_get(btn, ecore_file_file_get(options->path));
elm_layout_signal_callback_add(btn, "mouse,clicked,1", "*", _promote, item);
elm_layout_signal_callback_add(btn, "elm,deleted", "elm", _closetab, item);
evas_object_size_hint_min_set(btn, width * elm_config_scale_get(), h);
elm_box_pack_end(panel->tabs, btn);
evas_object_show(btn);
elm_box_recalculate(panel->tabs);
if (!options->background)
edi_mainview_panel_item_select(panel, item);
// Set focus on the newly opening window so that one can just start typing
editor = (Edi_Editor *)evas_object_data_get(content, "editor");
if (editor)
{
evas_object_show(editor->entry);
elm_object_focus_set(editor->entry, EINA_TRUE);
code = elm_code_widget_code_get(editor->entry);
editor->save_time = ecore_file_mod_time(elm_code_file_path_get(code->file));
editor->modified = EINA_FALSE;
}
if (options->line)
{
if (options->character > 1)
edi_mainview_panel_goto_position(panel, options->line, options->character);
else
edi_mainview_panel_goto(panel, options->line);
}
_edi_project_config_tab_add(options->path, mime?mime:options->type, EINA_FALSE, id);
}
static void
_edi_popup_cancel_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
evas_object_del((Evas_Object *) data);
}
static void
_edi_mainview_panel_mime_content_safe_popup(void)
{
Evas_Object *popup, *box, *table, *label, *button, *icon, *sep;
popup = elm_popup_add(_main_win);
elm_object_part_text_set(popup, "title,text",
_("Unrecognized file type"));
table = elm_table_add(popup);
icon = elm_icon_add(table);
elm_icon_standard_set(icon, "dialog-warning");
evas_object_size_hint_min_set(icon, 48 * elm_config_scale_get(), 48 * elm_config_scale_get());
evas_object_size_hint_weight_set(icon, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(icon, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_show(icon);
elm_table_pack(table, icon, 0, 0, 1, 1);
box = elm_box_add(popup);
sep = elm_separator_add(box);
elm_separator_horizontal_set(sep, EINA_TRUE);
evas_object_show(sep);
elm_box_pack_end(box, sep);
label = elm_label_add(popup);
elm_object_text_set(label, _("To force open, select this file in the file browser, <br>and use \"open as\" menu options."));
evas_object_show(label);
elm_table_pack(table, label, 1, 0, 1, 1);
evas_object_show(table);
elm_box_pack_end(box, table);
sep = elm_separator_add(box);
elm_separator_horizontal_set(sep, EINA_TRUE);
evas_object_show(sep);
elm_box_pack_end(box, sep);
evas_object_show(box);
elm_object_content_set(popup, box);
button = elm_button_add(popup);
elm_object_text_set(button, _("OK"));
elm_object_part_content_set(popup, "button1", button);
evas_object_smart_callback_add(button, "clicked", _edi_popup_cancel_cb, popup);
_edi_mainview_panel_current_tab_show(edi_mainview_panel_current_get());
evas_object_show(popup);
}
void
edi_mainview_panel_close(Edi_Mainview_Panel *panel)
{
Edi_Mainview_Item *item