From f56292b6cfb3799f3036e3784782878db6ab00a1 Mon Sep 17 00:00:00 2001 From: Vedran Ljubovic Date: Thu, 23 Aug 2007 10:54:12 +0000 Subject: [PATCH] Split FileView properly into files. Slowly it is getting its final form. --- efiler/EDE_FileDetailsView.cpp | 443 +++++++++++ efiler/EDE_FileDetailsView.h | 166 +++++ efiler/EDE_FileIconView.cpp | 648 ++++++++++++++++ efiler/EDE_FileIconView.h | 204 +++++ efiler/EDE_FileView.h | 1286 ++------------------------------ efiler/efiler.cpp | 6 +- efiler/fileops.h | 2 +- 7 files changed, 1524 insertions(+), 1231 deletions(-) create mode 100644 efiler/EDE_FileDetailsView.cpp create mode 100644 efiler/EDE_FileDetailsView.h create mode 100644 efiler/EDE_FileIconView.cpp create mode 100644 efiler/EDE_FileIconView.h diff --git a/efiler/EDE_FileDetailsView.cpp b/efiler/EDE_FileDetailsView.cpp new file mode 100644 index 0000000..a7a50b4 --- /dev/null +++ b/efiler/EDE_FileDetailsView.cpp @@ -0,0 +1,443 @@ +/* + * $Id$ + * + * EDE FileView class + * Part of edelib. + * Copyright (c) 2005-2007 EDE Authors. + * + * This program is licenced under terms of the + * GNU General Public Licence version 2 or newer. + * See COPYING for details. + */ + + +#include "EDE_FileView.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "EDE_Browser.h" + + +// debugging help +#define DBG "FileDetailsView: " + + +// -------------------------------------- +// +// File renaming stuff +// +// -------------------------------------- + + +// EditBox is displayed when renaming a file +// We subclass Fl_Input so we can handle keyboard +int FileDetailsView::EditBox::handle (int e) { + FileDetailsView* view = (FileDetailsView*)parent(); + if (e==FL_KEYBOARD && visible()) { + int k = Fl::event_key(); + if (Fl::event_key()==FL_Enter && visible()) { + view->hide_editbox(); + view->finish_rename(); + } else if (k==FL_Up || k==FL_Down || k==FL_Page_Up || k==FL_Page_Down) { + view->hide_editbox(); + return 0; // let the view scroll + } else if (k==FL_F+2 || k==FL_Escape) + view->hide_editbox(); + else + Fl_Input::handle(e); + + return 1; // don't send keys to view + } + if (e==FL_MOUSEWHEEL && visible()) view->hide_editbox(); + return Fl_Input::handle(e); +} + + +// show editbox at specified row and make the row "invisible" (bgcolor same as fgcolor) +void FileDetailsView::show_editbox(int row) { + if (!rename_callback_) return; + if (row<1 || row>size()) return; // nothing selected + if (text(row)[0]=='.' && text(row)[1]=='.' && text(row)[2]==column_char()) return; // can't rename "go up" button + + // unselect the row with focus - it's prettier that way + select(row,0); + + // Copy filename to editbox + char* filename = strdup(text(row)); + char* tmp = strchr(filename, column_char()); + if (tmp) *tmp='\0'; + editbox_->value(filename); + bucket.add(filename); + + // make the row "invisible" + editbox_row=row; + char* ntext = (char*)malloc(sizeof(char)*(strlen(text(row))+8)); // add 7 places for format chars + strncpy(ntext+7, text(row), strlen(text(row))); + strncpy(ntext, "@C255@.", 7); + ntext[strlen(text(row))+7]='\0'; + text(row,ntext); + bucket.add(ntext); + + + // calculate location for editbox + // "Magic constants" here are just nice spacing + int X=x()+get_icon(row)->w()+8; + int W=150; + int H=get_icon(row)->h()+2; + int Y=y()+2 - position(); + void* item = item_first(); + for (int i=1; iresize(X,Y,W,H); + editbox_->show(); + editbox_->take_focus(); + redraw(); + editbox_->redraw(); +} + + +void FileDetailsView::hide_editbox() { +EDEBUG(DBG "hide_editbox()\n"); + editbox_->hide(); + + // Make the edited row visible again + char* ntext = (char*)malloc(sizeof(char)*(strlen(text(editbox_row))-6)); // remove 7 places for format chars + strncpy(ntext, text(editbox_row)+7, strlen(text(editbox_row))-7); + ntext[strlen(text(editbox_row))-7]='\0'; + text(editbox_row,ntext); + bucket.add(ntext); +} + + +// This is called to actually rename file (when user presses Enter in editbox) +void FileDetailsView::finish_rename() { +EDEBUG(DBG "finish_rename()\n"); + rename_callback_(editbox_->value()); +} + + + + + + + + +// -------------------------------------- +// +// FileDetailsView +// +// -------------------------------------- + + +// ctor + +FileDetailsView::FileDetailsView(int X, int Y, int W, int H, char*label) : EDE_Browser(X,Y,W,H,label) { +// browser = new EDE_Browser(X,Y,W,H,label); +// browser->end(); +// end(); +// resizable(browser->the_scroll()); +// resizable(browser->the_group()); +// resizable(browser); + + // EDE_Browser properties + const int cw[]={250,200,70,130,100,0}; + column_widths(cw); + column_char('\t'); + column_header(_("Name\tType\tSize\tDate\tPermissions")); + const SortType st[]={ALPHA_CASE_SORT, ALPHA_CASE_SORT, FILE_SIZE_SORT, DATE_SORT, ALPHA_SORT, NO_SORT}; + column_sort_types(st); + when(FL_WHEN_ENTER_KEY_ALWAYS); + //when(FL_WHEN_ENTER_KEY_CHANGED); + //first=0; + + editbox_ = new EditBox(0, 0, 0, 0); + editbox_->box(FL_BORDER_BOX); + editbox_->parent(this); + editbox_->hide(); + + rename_callback_ = 0; + paste_callback_ = 0; + context_callback_ = 0; +} + + +// Methods for mainpulating items +// They are named similarly to browser methods, except that they take +// struct FileItem (defined in EDE_FileView.h) + +void FileDetailsView::insert(int row, FileItem *item) { + // Construct browser line + edelib::String value; + value = item->name+"\t"+item->description+"\t"+item->size+"\t"+item->date+"\t"+item->permissions; + char* realpath = strdup(item->realpath.c_str()); + EDE_Browser::insert(row, value.c_str(), realpath); // put realpath into data + bucket.add(realpath); +EDEBUG(DBG "value: %s\n", value.c_str()); + + // Get icon + edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_TINY); + if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_TINY,edelib::ICON_CONTEXT_MIMETYPE); + set_icon(row, Fl_Shared_Image::get(icon.c_str())); +} + + +void FileDetailsView::update(FileItem *item) { + int row=findrow(item->realpath); + if (row==0) return; + + //EDE_Browser::remove(row); + //insert(row, item); + // the above was reimplemented because a) it's unoptimized, b) adds everything at the end, + // c) causes browser to lose focus, making it impossible to click on something while + // directory is loading + + edelib::String value; + value = item->name+"\t"+item->description+"\t"+item->size+"\t"+item->date+"\t"+item->permissions; + char* realpath = strdup(item->realpath.c_str()); + text(row, value.c_str()); + data(row, realpath); + bucket.add(realpath); +EDEBUG(DBG "value: %s\n", value.c_str()); + + // Get icon + edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_TINY); + if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_TINY,edelib::ICON_CONTEXT_MIMETYPE); + set_icon(row, Fl_Shared_Image::get(icon.c_str())); +} + +// This method is needed because update() uses path to find item in list +void FileDetailsView::update_path(const char* oldpath,const char* newpath) { + int row=findrow(oldpath); + if (row==0) return; + char* c = strdup(newpath); + data(row,c); + bucket.add(c); +} + + + +// Make row look "disabled" (actually we use it for cut operation) + +void FileDetailsView::gray(int row) { + if (text(row)[0] == '@' && text(row)[1] == 'C') return; // already greyed + + char *ntext = (char*)malloc(sizeof(char)*(strlen(text(row))+7)); // add 6 places for format chars + strncpy(ntext+6, text(row), strlen(text(row))); + strncpy(ntext, "@C25@.", 6); + ntext[strlen(text(row))+6]='\0'; + text(row,ntext); + bucket.add(ntext); + + // grey icon - but how to ungray? + Fl_Image* im = get_icon(row)->copy(); + im->inactive(); + set_icon(row,im); +} + +void FileDetailsView::ungray(int row) { + if (text(row)[0] != '@' || text(row)[1] != 'C') return; // not greyed + + char *ntext = (char*)malloc(sizeof(char)*(strlen(text(row))-5)); // remove 6 places for format chars + strncpy(ntext, text(row)+6, strlen(text(row))-6); + ntext[strlen(text(row))-6]='\0'; + text(row,ntext); + bucket.add(ntext); + + // doesn't work + Fl_Image* im = get_icon(row)->copy(); + //im->refresh(); + //im->uncache(); // doesn't work + //im->color_average(FL_BACKGROUND_COLOR, 1.0); + //im->data(im->data(),im->count()); + set_icon(row,im); + + //redraw(); // OPTIMIZE +} + + + +// Overloaded handle for: context menu, file renaming and dnd support + +int FileDetailsView::handle(int e) { +//EDEBUG(DBG"Event: %d\n", e); + + /* ------------------------------ + Right-click event + --------------------------------- */ + + if (e==FL_RELEASE && Fl::event_button()==3) { + void* item = item_first(); + int itemy=y()-position(); + int i; + for (i=1; i<=size(); i++) { + itemy+=item_height(item); + if (itemy>Fl::event_y()) break; + } + + set_focus(i); + Fl::event_is_click(0); // prevent doubleclicking with right button + if (context_callback_) context_callback_(this, data(i)); + return 1; + } + + + /* ------------------------------ + Rename support + --------------------------------- */ + + // If rename_callback_ isn't set, we don't support renaming + if (rename_callback_) { + + // hide editbox when user clicks outside of it + if (e==FL_PUSH && editbox_->visible() && !Fl::event_inside(editbox_)) + hide_editbox(); + + // hide editbox when scrolling mouse + if (e==FL_MOUSEWHEEL && editbox_->visible()) + hide_editbox(); + + + static bool renaming=false; + // this variable is used because we want to select item on FL_PUSH, but + // actually start editing on FL_RELEASE (otherwise it would be impossible + // to doubleclick) + + if (e==FL_PUSH) renaming=false; + + // Click once on item that is already selected AND focused to rename it + if (e==FL_PUSH && !editbox_->visible() && Fl::event_clicks()==0 && Fl::event_button()==1) { + + // we're only interested in the first column + // columns 2-5 are "safe haven" where user can click without renaming anything + const int* l = column_widths(); + if (Fl::event_x()x()+l[0]) + return EDE_Browser::handle(e); + + // Is clicked item also focused? + void* item = item_first(); + int focusy=y()-position(); + for (int i=1; iFl::event_y()) break; + item=item_next(item); + } + if (Fl::event_y()focusy+item_height(item)) + // It isn't + return EDE_Browser::handle(e); + if (selected(get_focus())!=1) + // If it's focused but not selected, then this action is select + return EDE_Browser::handle(e); + + renaming=true; // On next event, we will check if it's doubleclick + } + + if (renaming && (e==FL_DRAG || e==FL_RELEASE || e==FL_MOVE) && Fl::event_is_click()==0) { + // Fl::event_is_click() will be >0 on doubleclick + show_editbox(get_focus()); + renaming=false; + // don't pass mouse event, otherwise item will become reselected which is a bit ugly + return 1; + } + } + + + /* ------------------------------ + Drag&drop support + --------------------------------- */ + + // If paste_callback_ isn't set, we can't support dnd + if (paste_callback_) { + + // Let others know that we accept dnd + if (e==FL_DND_ENTER||e==FL_DND_DRAG) return 1; + + // Scroll the view by dragging to border (only works on heading... :( ) + if (e==FL_DND_LEAVE) { + if (Fl::event_y()y()+h()) + position(position()+1); + return 1; + } + + // Don't unselect items on FL_PUSH cause that could be dragging + if (e==FL_PUSH && Fl::event_clicks()!=1) return 1; + + static int dragx,dragy; // to check min drag distance + if (e==FL_DRAG) { + edelib::String selected_items; + for (int i=1; i<=size(); i++) + if (selected(i)==1) { + selected_items += "file://"; + selected_items += (char*)data(i); + selected_items += "\r\n"; + } +EDEBUG(DBG "DnD buffer: '%s'\n", selected_items.c_str()); + Fl::copy(selected_items.c_str(),selected_items.length(),0); + Fl::dnd(); + dragx = Fl::event_x(); dragy = Fl::event_y(); + return 1; // don't do the multiple selection thing from Fl_Browser + } + + static bool dndrelease=false; // so we know if paste is from dnd or not + if (e==FL_DND_RELEASE) { +EDEBUG(DBG "FL_DND_RELEASE '%s'\n", Fl::event_text()); + + // Sometimes drag is accidental + if (abs(Fl::event_x()-dragx)>MIN_DISTANCE_FOR_DND || abs(Fl::event_y()-dragy)>MIN_DISTANCE_FOR_DND) { + dndrelease=true; + Fl::paste(*this,0); + } + return 0; + // return 1 would call Fl::paste(*belowmouse(),0) (see fl_dnd_x.cxx around line 168). + // In our case that could be catastrophic + } + + if (e==FL_PASTE) { +EDEBUG(DBG "FL_PASTE\n"); + if (!Fl::event_text() || !Fl::event_length()) return 1; +EDEBUG(DBG "-- '%s' (%d)\n",Fl::event_text(),Fl::event_length()); + + // Paste is coming from menu/keyboard + if (!dndrelease) { + paste_callback_(0); // 0 = current dir + return 1; + } + dndrelease=false; + + // Where is user dropping? + // If it's not the first column, we assume current directory + const int* l = column_widths(); + if (Fl::event_x()x()+l[0]) { + paste_callback_(0); + return 1; + } + + // Find item where stuff is dropped + void* item = item_first(); + int itemy=y()-position(); + int i; + for (i=1; i<=size(); i++) { + itemy+=item_height(item); + if (itemy>Fl::event_y()) break; + } + + paste_callback_((const char*)data(i)); + return 1; + } + } // if (paste_callback_) ... + + return EDE_Browser::handle(e); +} + + +/* $Id */ diff --git a/efiler/EDE_FileDetailsView.h b/efiler/EDE_FileDetailsView.h new file mode 100644 index 0000000..c7f78c5 --- /dev/null +++ b/efiler/EDE_FileDetailsView.h @@ -0,0 +1,166 @@ +/* + * $Id$ + * + * EDE FileView class + * Part of edelib. + * Copyright (c) 2005-2007 EDE Authors. + * + * This program is licenced under terms of the + * GNU General Public Licence version 2 or newer. + * See COPYING for details. + */ + + +#ifndef EDE_FileDetailsView_H +#define EDE_FileDetailsView_H + + +#include + +#include + +#include "EDE_Browser.h" +//#include "EDE_FileView.h" + + + +// Sometimes I accidentaly "drag" an icon - don't want dnd to trigger +#define MIN_DISTANCE_FOR_DND 10 + + +class FileDetailsView : public EDE_Browser { +private: +// EDE_Browser* browser; - yada + // internal list of items +/* struct myfileitem { + struct FileItem *item; + myfileitem* previous; + myfileitem* next; + } *firstitem;*/ + + // EditBox is displayed when renaming a file + // We subclass Fl_Input so we can handle keyboard events + class EditBox : public Fl_Input { + public: + EditBox(int x, int y, int w, int h, const char* label = 0) : Fl_Input(x,y,w,h,label) {} + int handle (int e); + }* editbox_; + int editbox_row; + + // show editbox at specified row and make the row "invisible" (bgcolor same as fgcolor) + void show_editbox(int row); + void hide_editbox(); + + // This is called to actually rename file (when user presses Enter in editbox) + void finish_rename(); + + + // Find row number corresponding to realpath (encoded in data()) + int findrow(edelib::String realpath) { + for (int i=1; i<=size(); i++) { + char *tmp = (char*)data(i); + if (realpath==tmp) return i; + } + return 0; + } + + // Callbacks + rename_callback_type* rename_callback_; + paste_callback_type* paste_callback_; + Fl_Callback* context_callback_; + + + + // Deallocator class is used to prevent memleaks + // It stores pointers to allocated memory that will be cleaned up later + // Just remember to call empty() when needed - everything else is automatic :) + + // -- The reason we need such class is that there are no insert_copy() or + // data_copy() methods in Fl_Browser (cf. label_copy() in Fl_Widget) + + #define MEM_STEP 1000 + class Deallocator { + void** items; + int size, capacity; + public: + Deallocator() : size(0), capacity(MEM_STEP) { + items = (void**)malloc(sizeof(void*)*capacity); + for (int i=0; i=capacity) { + capacity+=MEM_STEP; + items = (void**)realloc(items,sizeof(void*)*capacity); + for (int i=capacity-MEM_STEP; irealpath); + if (row) EDE_Browser::remove(row); + } + void remove(int row) { EDE_Browser::remove(row); } // doesnt work without this method :( + void update(FileItem *item); // refresh one row without reloading the whole list + + // This method is needed because update() uses path to find item in list + void update_path(const char* oldpath,const char* newpath); + + // Make row look "disabled" (actually we use it for cut operation) + void gray(int row); + void ungray(int row); + + // Overloaded handle for: context menu, file renaming and dnd support + int handle(int e); + + // Callback when user renames a file (takes const char* filename) + void rename_callback(rename_callback_type* cb) { rename_callback_ = cb; } + + // Callback that is called by FL_PASTE event (such as drag&drop) + // (takes const char* paste_target /e.g. target directory or 0 for current directory/) + void paste_callback(paste_callback_type* cb) { paste_callback_ = cb; } + + // Callback that is called on right mouse click (for e.g. context menu) + void context_callback(Fl_Callback* cb) { context_callback_ = cb; } + + // Avoid memory leak + void clear() { + bucket.empty(); + EDE_Browser::clear(); + } + + int visible() { return Fl_Widget::visible(); } + + // Aliases for better compatibility with other views + const char*path(int row) { return (const char*)data(row); } + void show_item(int row) { middleline(row); } +}; + + + + +#endif + +/* $Id */ diff --git a/efiler/EDE_FileIconView.cpp b/efiler/EDE_FileIconView.cpp new file mode 100644 index 0000000..5ae5f77 --- /dev/null +++ b/efiler/EDE_FileIconView.cpp @@ -0,0 +1,648 @@ +/* + * $Id$ + * + * EDE FileView class + * Part of edelib. + * Copyright (c) 2005-2007 EDE Authors. + * + * This program is licenced under terms of the + * GNU General Public Licence version 2 or newer. + * See COPYING for details. + */ + + +#include "EDE_FileView.h" + + +#include +#include // for FL_PATH_MAX +#include // for fl_measure and drawing selection box +#include + +#include +#include +#include +#include + + +// debugging help +#define DBG "FileIconView: " + + +// ctor + +#ifdef USE_FLU_WRAP_GROUP +FileIconView::FileIconView(int X, int Y, int W, int H, char*label) : Flu_Wrap_Group(X,Y,W,H,label) +#else +FileIconView::FileIconView(int X, int Y, int W, int H, char*label) : edelib::ExpandableGroup(X,Y,W,H,label) +#endif +{ + end(); + box(FL_DOWN_BOX); + color(FL_BACKGROUND2_COLOR); +#ifdef USE_FLU_WRAP_GROUP + spacing(ICON_SPACING,ICON_SPACING); +#endif + + select_x1=select_y1=select_x2=select_y2=0; + focused=0; + m_selected = 0; + + rename_callback_ = 0; + paste_callback_ = 0; + context_callback_ = 0; +} + + +// Methods for manipulating items +// They are named similarly to Browser methods, except that they take +// struct FileItem (defined in EDE_FileView.h) + +void FileIconView::insert(int row, FileItem *item) { + // update list of selected[] items + if (!m_selected) + m_selected = (int*)malloc(sizeof(int)*(children()+1)); + else + m_selected = (int*)realloc(m_selected,sizeof(int)*(children()+1)); + m_selected[children()]=0; + + Fl_Button* b = new Fl_Button(0,0,ICONW,ICONH); + b->box(FL_RFLAT_BOX); + b->color(FL_BACKGROUND2_COLOR); + b->align(FL_ALIGN_INSIDE|FL_ALIGN_CENTER|FL_ALIGN_CLIP); + + // Set the label + char buffer[FL_PATH_MAX]; + uint j=0; + for (uint i=0; iname.length(); i++,j++) { + buffer[j] = item->name[i]; + buffer[j+1] = '\0'; + if (buffer[j] == '@') { // escape @ + buffer[++j] = '@'; + buffer[j+1] = '\0'; + } + b->copy_label(buffer); + int lw =0, lh = 0; + fl_measure(b->label(), lw, lh); + if (lw>ICONW) { + if (j==i+2) { // already added 2 newlines + buffer[j-2]='.'; + buffer[j-1]='.'; + buffer[j]='.'; + buffer[j+1]='\0'; + j+=2; + break; // for + } else { // add newline + buffer[j+1]=buffer[j]; + buffer[j]='\n'; + buffer[j+2]='\0'; + j++; + } + } + } + while (j<=item->name.length()+2){ + buffer[j++]='\n'; + //buffer[j++]='L'; + } + buffer[j]='\0'; + b->copy_label(buffer); + + // Tooltip text + edelib::String tooltip = _("Name: ")+item->name; + if (item->size != "") tooltip += _("\nSize: ")+item->size; + tooltip += _("\nType: ")+item->description+_("\nDate: ")+item->date+_("\nPermissions: ")+item->permissions; + b->tooltip(strdup(tooltip.c_str())); + b->user_data(strdup(item->realpath.c_str())); + + // Set icon + edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_MEDIUM); + if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_MEDIUM,edelib::ICON_CONTEXT_MIMETYPE); + b->image(Fl_Shared_Image::get(icon.c_str())); + +#ifdef USE_FLU_WRAP_GROUP + Flu_Wrap_Group::insert(*b,row); +#else + edelib::ExpandableGroup::insert(*b,row); +#endif + //insert(*b,row); -- why doesn't this work? + redraw(); +} + +void FileIconView::remove(FileItem *item) { + Fl_Button* b = find_button(item->realpath); + if (b) { +#ifdef USE_FLU_WRAP_GROUP + Flu_Wrap_Group::remove(*b); // note that FWG requires to dereference the pointer +#else + edelib::ExpandableGroup::remove(b); +#endif + //remove(b); + delete b; + } +} + +void FileIconView::remove(int row) { + if (row<1 || row>children()) return; + Fl_Button* b = (Fl_Button*)child(row-1); +#ifdef USE_FLU_WRAP_GROUP + Flu_Wrap_Group::remove(*b); // note that FWG requires to dereference the pointer +#else + edelib::ExpandableGroup::remove(b); +#endif + //remove(b); + delete b; +} + + +void FileIconView::update(FileItem *item) { + Fl_Button* b = find_button(item->realpath); + if (!b) return; + + // Tooltip text + edelib::String tooltip = _("Name: ")+item->name+_("\nSize: ")+item->size+_("\nType: ")+item->description+_("\nDate: ")+item->date+_("\nPermissions: ")+item->permissions; + b->tooltip(strdup(tooltip.c_str())); + + // Set icon + edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_MEDIUM); + if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_MEDIUM,edelib::ICON_CONTEXT_MIMETYPE); + b->image(Fl_Shared_Image::get(icon.c_str())); + + b->redraw(); +} + +// This method is needed because update() uses path to find item in list +void FileIconView::update_path(const char* oldpath,const char* newpath) { + Fl_Button* b = find_button(oldpath); + if (!b) return; + b->user_data(strdup(newpath)); +} + +// Select item (if value==1) or unselect (if value==0) +// We are faking selection by manipulating fg & bg color +// TODO: create a new class for icons that should handle selection internally +void FileIconView::select(int row, int value) { + if (row<1 || row>children()) return; + if (!m_selected) return; // shouldn't happen + set_focus(row); + int i=0; + Fl_Button* b = (Fl_Button*)child(row-1); + if (value) { + while (m_selected[i++]!=0); + m_selected[i-1]=row; + + b->color(FL_SELECTION_COLOR); + b->labelcolor(fl_contrast(FL_FOREGROUND_COLOR,FL_SELECTION_COLOR)); + b->redraw(); + } else { + while (m_selected[i]!=0) { + if (m_selected[i]==row) { + int j=i; + while (m_selected[j]!=0) { + m_selected[j]=m_selected[j+1]; + j++; + } + break; + } + i++; + } + + b->color(FL_BACKGROUND2_COLOR); + b->labelcolor(FL_FOREGROUND_COLOR); + b->redraw(); + } +} + +// Scroll the view until item becomes visible +void FileIconView::show_item(int row) { +EDEBUG(DBG"show_item(%d)\n", row); + if (row<1 || row>children()) return; + Fl_Button* b = (Fl_Button*)child(row-1); + +#ifdef USE_FLU_WRAP_GROUP + // scroll_to() will scroll until icon is at the top of view + // I prefer that view is scrolled just enough so icon is visible + // FIXME: sometimes b->y() is so large that it wraps around and becomes positive + if (b->y()+b->h() > y()+h()) { + int scrollto = scrollbar.value() + (b->y()+b->h()-y()-h())+1; +EDEBUG(DBG"by: %d bh: %d y: %d h: %d sv: %d\n", b->y(), b->h(), y(), h(), scrollbar.value()); + ((Fl_Valuator*)&scrollbar)->value(scrollto); + redraw(); + draw(); // we need to relayout the group so that b->y() value is updated for next call + } + if (b->y() < y()) + scroll_to(b); +#else + // ExpandableGroup scrolling - not tested and probably broken + Fl_Scrollbar* s = get_scroll(); + if (b->y() > s->value()+h()) { + // Widget is below current view + scrolly(b->y()+b->h()-h()); + } else if (b->y() < s->value()) { + // Widget is above current view + scrolly(b->y()); + } + // else { widget is visible, do nothing } +#endif +} + + +// Overloaded handle() method +// it's currently a bit messy, see implementation comments for details +int FileIconView::handle(int e) { +//EDEBUG(DBG"Event %d\n",e); + + // ------------------------------------------------ + // Fixes for focus management + // ------------------------------------------------ + + // fltk provides focus management for members of Fl_Group, but it has minor problems: + // - after switching to another window and back, focused widget is forgotten + // - if no widget is focused, arrow keys will navigate outside group + // - Tab has same effect as right arrow - we want arrows to only be used inside + // group, and (Shift+)Tab to be used for going outside + + if (Fl::focus()->inside(this)) { // is focus inside? + int k = Fl::event_key(); +//EDEBUG(DBG"event_key: %d\n",k); + if (k==FL_Up || k==FL_Down || k==FL_Left || k==FL_Right) { +#ifdef USE_FLU_WRAP_GROUP + // Wrap around - FWG only (due to methods such as above()) + // FL_KEYDOWN happens only if key is otherwise unhandled + if (e==FL_KEYDOWN) { + int x = get_focus()-1; + Fl_Widget* b = child(x); + Fl_Widget* b2; + + switch(k) { + case FL_Up: + b2=above(b); + if (x==0) b2=child(children()-1); + break; + case FL_Down: + b2=below(b); + // Bug in Flu_Wrap_Group - inconsistent behavior of below(): + if (b2==b) b2=child(children()-1); + if (x==children()-1) b2=child(0); + break; + case FL_Left: + b2=left(b); + if (x==0) b2=child(children()-1); + break; + case FL_Right: + b2=next(b); + if (x==children()-1) b2=child(0); + break; + default: // to silence compiler warnings + break; + } + + for (int i=0; ilabel()); + // Try to find another widget to give focus to. + // Because widgets can be *very* nested, we go straight + // to window() + + Fl_Group* parent_=(Fl_Group*)window(); + Fl_Widget* jumpto=0; + int i=0; + while (jumpto==0 && parent_!=this) { + jumpto=parent_->child(i); +EDEBUG(DBG" -- (%s)\n", jumpto->label()); + if (this->inside(jumpto) || !jumpto->visible()) + jumpto=0; + i++; + if (i==parent_->children()) { + int j; + for (j=0; jchildren(); j++) + if (parent_->child(j)->visible()) break; + if (j==parent_->children()) { + // nothing is visible!? +EDEBUG(DBG"WTF!\n"); + parent_=(Fl_Group*)this; + } else { + parent_=(Fl_Group*)parent_->child(j); +EDEBUG(DBG" -> [%s]\n", parent_->label()); // label everything to debug!!! + } + i=0; + } + if (jumpto!=0 && !jumpto->take_focus()) + jumpto=0; // widget refused focus, keep searching + } +EDEBUG(DBG"[X]\n"); + // if this is the only widget, do nothing + return 1; + } + + } // if (Fl::focus()->inside(this)) ... + + // Restore focused widget after losing focus + if (e==FL_FOCUS) { + if (focused) set_focus(focused); + } + + + + // ------------------------------------------------ + // Make Fl_Group behave like proper widget + // ------------------------------------------------ + + // We accept focus (this defaults to 0 with FL_GROUP) + if (e==FL_FOCUS) return 1; + + // We accept mouse clicks as long as they're inside + if (e==FL_PUSH && Fl::event_x()>x() && Fl::event_x() < x()+w()-Fl::scrollbar_size()) return 1; + + // Do callback on enter key + // (because icons don't have callbacks) + if (e==FL_KEYBOARD && Fl::event_key()==FL_Enter) { + do_callback(); + return 1; + } + + // Select/unselect toggle using SPACE + if ((e==FL_KEYBOARD || e==FL_KEYUP) && Fl::event_key()==' ') { + if (selected(get_focus())) + select(get_focus(),0); + else + select(get_focus(),1); + return 1; + } + + + // ------------------------------------------------ + // FL_DRAG does two things: laso and dnd + // ------------------------------------------------ + + // "laso" a.k.a. selection box is common name for box used to select many widgets at once + + static bool laso=false; // are we laso-ing? + static int dragx,dragy; // to check for min dnd distance + if (e==FL_DRAG) { +EDEBUG(DBG"FL_DRAG! "); + + // No laso is active, either start laso or start dnd operation + if (!laso) { + // Drag inside child is dnd + int ex=Fl::event_x(); int ey=Fl::event_y(); + int inside=0; + for (int i=0; ib->x()+SELECTION_EDGE && exx()+b->w()-SELECTION_EDGE && ey>b->y()+SELECTION_EDGE && eyy()+b->h()-SELECTION_EDGE) { + inside=i+1; break; + } + } + if (inside) { + // If widget isn't selected, unselect everything else and select this + if (!selected(inside)) { + for (int i=0;icolor(FL_BACKGROUND2_COLOR); + b->labelcolor(FL_FOREGROUND_COLOR); + } + int i=0; + while (m_selected[i]!=0) m_selected[i++]=0; + select(inside,1); + redraw(); // show changes in selection state + } + + // Construct dnd string and start dnd + edelib::String selected_items; + for (int i=1; i<=children(); i++) + if (selected(i)==1) { + selected_items += "file://"; + selected_items += path(i); + selected_items += "\r\n"; + } + + // If paste_callback_ isn't set, that means we don't support dnd + if (paste_callback_) { + Fl::copy(selected_items.c_str(),selected_items.length(),0); + Fl::dnd(); + dragx=ex; dragy=ey; // to test if its close + } + return 1; + + } else { // if(inside)... + + // Drag to select (a.k.a. "laso") operation (outside widgets) + // Draw a dashed line around icons +EDEBUG(DBG"- laso begin.\n"); + laso=true; + // Set coordinates for selection box (drawn in draw()) + select_x1=select_x2=Fl::event_x(); + select_y1=select_y2=Fl::event_y(); + } + + + } else { // if (!laso) ... + + // Ongoing laso operation, update selection coordinates + select_x2=Fl::event_x(); + select_y2=Fl::event_y(); + redraw(); +EDEBUG(DBG"- laso box (%d,%d,%d,%d).\n",select_x1,select_y1,select_x2,select_y2); + } + return 1; + } // if (e==FL_DRAG) ... + + + + + + // ------------------------------------------------ + // FL_RELEASE does many things: + // - (un)select items, + // - terminate laso operation, + // - show context menu, + // - doubleclick, + // ------------------------------------------------ + + + // Mouse button released + if (e==FL_RELEASE) { +EDEBUG(DBG"FL_RELEASE! "); + + // Unselect everything unless Shift or Ctrl is held + if (!Fl::event_state(FL_SHIFT) && !Fl::event_state(FL_CTRL)) { + for (int i=0;icolor(FL_BACKGROUND2_COLOR); + b->labelcolor(FL_FOREGROUND_COLOR); + } + redraw(); + int i=0; + while (m_selected[i]!=0) m_selected[i++]=0; + } + + // Stop laso operation + if (laso) { +EDEBUG(DBG"- stop laso.\n"); + laso=false; + + // Order coordinates + int tmp; + if (select_x1>select_x2) { tmp=select_x1; select_x1=select_x2; select_x2=tmp; } + if (select_y1>select_y2) { tmp=select_y1; select_y1=select_y2; select_y2=tmp; } + + // Exclude edges + select_x1 += SELECTION_EDGE; + select_y1 += SELECTION_EDGE; + select_x2 -= SELECTION_EDGE; + select_y2 -= SELECTION_EDGE; +EDEBUG(DBG"After fixing the box coords: (%d,%d,%d,%d)\n", select_x1, select_y1, select_x2, select_y2); + + // Find which buttons were lasoed + int i; + for (i=0; ix()+w->w(); + int wy2 = w->y()+w->h(); + if (select_x2>w->x() && select_x1w->y() && select_y1label(), w->x(), w->y(), wx2, wy2); + } + } + // Shift key is the same as Ctrl with laso + + select_x1=select_x2=select_y1=select_y2=0; + redraw(); + + // Single click + } else { // if(laso)... + + // Find child that was clicked + int i; + for (i=0; itake_focus(); + focused=0; + } + } + + // Right button - call context menu + if (Fl::event_button() == 3) { + Fl::event_is_click(0); // prevent doubleclicking with right button + if (context_callback_) context_callback_(this, (void*)path(get_focus())); + } + + // Double-click operation + if (Fl::event_clicks()) { + do_callback(); + } + } + + + // ------------------------------------------------ + // Drag&drop support (apart from FL_DRAG) + // ------------------------------------------------ + + // If paste_callback_ isn't set, that means we don't support dnd + if (paste_callback_) { +if (e==FL_DND_ENTER) { EDEBUG(DBG"FL_DND_ENTER\n"); } +if (e==FL_DND_DRAG) { EDEBUG(DBG"FL_DND_DRAG\n"); } +if (e==FL_DND_RELEASE) { EDEBUG(DBG"FL_DND_RELEASE\n"); } + // Let the window manager know that we accept dnd + if (e==FL_DND_ENTER||e==FL_DND_DRAG) return 1; + +/* // Scroll the view by dragging to border + if (e==FL_DND_LEAVE) { + if (Fl::event_y()y()+h()) + position(position()+1); + return 1; + }*/ + + static bool dndrelease=false; + if (e==FL_DND_RELEASE) { +EDEBUG(DBG"FL_DND_RELEASE '%s'\n", Fl::event_text()); + // Sometimes drag is accidental + if (abs(Fl::event_x()-dragx)>MIN_DISTANCE_FOR_DND || abs(Fl::event_y()-dragy)>MIN_DISTANCE_FOR_DND) { + dndrelease=true; + Fl::paste(*this,0); + } + + return 0; + // return 1 would call Fl::paste(*belowmouse(),0) (see fl_dnd_x.cxx around line 168). + // In our case that could be catastrophic + } + + if (e==FL_PASTE) { +EDEBUG(DBG"FL_PASTE\n"); + if (!Fl::event_text() || !Fl::event_length()) return 1; +EDEBUG(DBG"1 '%s' (%d)\n",Fl::event_text(),Fl::event_length()); + + // Paste comes from menu/keyboard + if (!dndrelease) { + paste_callback_(0); // 0 = current dir + return 1; + } + dndrelease=false; + + // Where is the user dropping? + // It it's inside an item, try pasting + int ex=Fl::event_x(); int ey=Fl::event_y(); + for (int i=0; ib->x()+SELECTION_EDGE && exx()+b->w()-SELECTION_EDGE && ey>b->y()+SELECTION_EDGE && eyy()+b->h()-SELECTION_EDGE) { + paste_callback_(path(i+1)); + return 0; + } + } + + // Nothing found... assume current directory + paste_callback_(0); + return 0; + } + + } // if(paste_callback_)... + + + // End of handle() +#ifdef USE_FLU_WRAP_GROUP + return Flu_Wrap_Group::handle(e); +#else + return edelib::ExpandableGroup::handle(e); +#endif + +} + + +/* $Id */ diff --git a/efiler/EDE_FileIconView.h b/efiler/EDE_FileIconView.h new file mode 100644 index 0000000..764dcea --- /dev/null +++ b/efiler/EDE_FileIconView.h @@ -0,0 +1,204 @@ +/* + * $Id$ + * + * EDE FileView class + * Part of edelib. + * Copyright (c) 2005-2007 EDE Authors. + * + * This program is licenced under terms of the + * GNU General Public Licence version 2 or newer. + * See COPYING for details. + */ + + +#ifndef EDE_FileIconView_H +#define EDE_FileIconView_H + + +#include + +#include + +//#include "EDE_FileView.h" + + +// uncomment this to use edelib::ExpandableGroup +#define USE_FLU_WRAP_GROUP + +#ifdef USE_FLU_WRAP_GROUP + #include "Flu_Wrap_Group.h" +#else + #include +#endif + + + + +// Dimensions +#define ICONW 70 +#define ICONH 80 + +// Spacing to use between icons +#define ICON_SPACING 5 + +// Used to make edges of icons less sensitive to selection box, dnd and such +// (e.g. if selection laso only touches a widget with <5px, it will not be selected) +#define SELECTION_EDGE 5 + +// Sometimes I accidentaly "drag" an icon - don't want dnd to trigger +#define MIN_DISTANCE_FOR_DND 10 + + +#ifdef USE_FLU_WRAP_GROUP +class FileIconView : public Flu_Wrap_Group { +#else +class FileIconView : public edelib::ExpandableGroup { +#endif + +private: + // private vars for handling selection and focus + // because group doesn't do it + int focused; + int* m_selected; + + // callbacks + rename_callback_type* rename_callback_; + paste_callback_type* paste_callback_; + Fl_Callback* context_callback_; + + // selection box + int select_x1,select_y1,select_x2,select_y2; + + // Find the Fl_Button corresponding to real path (encoded in user_data()) + Fl_Button* find_button(edelib::String realpath) { + for (int i=0; iuser_data(); + if (realpath==tmp) return b; + } + return 0; + } + +public: + + FileIconView(int X, int Y, int W, int H, char*label=0); + + // Methods for manipulating items + // They are named similarly to Browser methods, except that they take + // struct FileItem (defined in EDE_FileView.h) + + // NOTE: For compatibility with Fl_Browser-based views, row is 1..children() + // as opposed to Fl_Group methods that use 0..children()-1 + // This is a fltk bug + + void insert(int row, FileItem *item); + void add(FileItem *item) { insert(children()+1, item); } + void remove(FileItem *item); + void remove(int row); + void update(FileItem *item); + + // This method is needed because update() uses path to find item in list + void update_path(const char* oldpath,const char* newpath); + + // Make row look "disabled" (actually we use it for cut operation) + void gray(int row) { + if (row<1 || row>children()) return; + Fl_Button* b = (Fl_Button*)child(row-1); + // FIXME this also means that item can't be selected + b->deactivate(); + } + void ungray(int row) { + if (row<1 || row>children()) return; + Fl_Button* b = (Fl_Button*)child(row-1); + b->activate(); + } + + // Callback when user renames a file (takes const char* filename) + void rename_callback(rename_callback_type* cb) { rename_callback_ = cb; } + + // Callback that is called by FL_PASTE event (such as drag&drop) + // (takes const char* paste_target /e.g. target directory or 0 for current directory/) + void paste_callback(paste_callback_type* cb) { paste_callback_ = cb; } + + // Callback that is called on right mouse click (for e.g. context menu) + void context_callback(Fl_Callback* cb) { context_callback_ = cb; } + + // Item real path (user_data) + const char* path(int row) { + if (row<1 || row>children()) return 0; + Fl_Widget* w = child(row-1); + return (const char*)w->user_data(); + } + + // Is item selected? + int selected(int row) { + if (row<1 || row>children()) return 0; + int i=0; + while(m_selected[i]!=0) + if (m_selected[i++]==row) return 1; + return 0; + } + + // Select item (if value==1) or unselect (if value==0) + void select(int row, int value); + + // Return nr. of widget that has keyboard focus + int get_focus() { + Fl_Widget* focus = Fl::focus(); + int i = find(focus); // a Fl_Group method + if (ichildren()) return; + Fl_Widget* w = child(row-1); + w->take_focus(); + show_item(row); + focused=row; + } + + // Scroll the view until item becomes visible + void show_item(int row); + + // Overloaded handle() method + // it's currently a bit messy, see implementation comments for details + int handle(int e); + + // Override draw() for selection box + void draw() { +#ifdef USE_FLU_WRAP_GROUP + Flu_Wrap_Group::draw(); +#else + edelib::ExpandableGroup::draw(); +#endif + if (select_x1>0 && select_y1>0) { + fl_color(33); + fl_line_style(FL_DASH); + fl_line(select_x1,select_y1,select_x1,select_y2); + fl_line(select_x1,select_y2,select_x2,select_y2); + fl_line(select_x2,select_y2,select_x2,select_y1); + fl_line(select_x2,select_y1,select_x1,select_y1); + fl_line_style(0); + } + } + + // Override clear so that we can empty selected & focused values + void clear() { + focused=0; + if (m_selected) free(m_selected); + m_selected = 0; +#ifdef USE_FLU_WRAP_GROUP + Flu_Wrap_Group::clear(); + scroll_to_beginning(); // move scrollbar to top +#else + edelib::ExpandableGroup::clear(); +#endif + } +}; + + +#endif + +/* $Id */ diff --git a/efiler/EDE_FileView.h b/efiler/EDE_FileView.h index d91f558..ddde338 100644 --- a/efiler/EDE_FileView.h +++ b/efiler/EDE_FileView.h @@ -10,36 +10,42 @@ * See COPYING for details. */ +/** + * \class FileView + * \brief Widget for displaying a list of files + * + * FileView is a generic widget that displays files in a + * certain way. To get/change the type of view, use method + * type() which sets or returns a value of type enum FileViewType. + * Allowed values are: + * FILE_LIST_VIEW - a multicolumn list of files (if necessary, + * a horizontal scrollbar will be shown) + * FILE_DETAILS_VIEW - each file is in a single row, and columns + * contain information such as file type, size, date of last change + * and permission + * FILE_ICON_VIEW - large icons are shown, and filenames are + * displayed below icons (if necessary, a vertical scrollbar will be + * shown), additional data is shown in tooltips + * + * Of course, you are free to directly use classes FileListView, + * FileDetailsView and FileIconView respectively (however, you have + * to include FileView.h to get them). + * + * Unlike some similar classes, FileView doesn't do any scanning of + * directories. Instead, a program using FileView should use methods + * such as add(), insert(), remove() and update(). These methods take + * values of type struct FileItem, which contains all the data shown + * by FileDetailsView. The reason for this is to enable reuse in + * programs that don't work with standard filesystem. + * + */ + #ifndef EDE_FileView_H #define EDE_FileView_H - -#define USE_FLU_WRAP_GROUP - - -#include // for FL_PATH_MAX -#include -#include -#include -#include -#include -#include - #include -#include -#include - -#include "EDE_Browser.h" - -#ifdef USE_FLU_WRAP_GROUP -# include "Flu_Wrap_Group.h" -#else -# include -#endif - - struct FileItem { edelib::String name; // just the name @@ -51,1195 +57,16 @@ struct FileItem { edelib::String permissions; }; - -// Type for rename_callback +// Type for callbacks // I don't know how to do this without creating a new type :( typedef void (rename_callback_type)(const char*); typedef void (paste_callback_type)(const char*); -#define ICONW 70 -#define ICONH 80 -// Spacing to use between icons -#define ICON_SPACING 5 -// How many pixels from selection edge will be ignored -// (e.g. if selection laso only touches a widget with <5px, it will not be selected) -#define SELECTION_EDGE 5 +#include "EDE_FileDetailsView.h" +#include "EDE_FileIconView.h" -// Sometimes I accidentaly "drag" an icon - don't want dnd to trigger -#define MIN_DISTANCE_FOR_DND 10 - - -#ifdef USE_FLU_WRAP_GROUP -class FileIconView_ : public Flu_Wrap_Group { -#else -class FileIconView_ : public edelib::ExpandableGroup { -#endif -private: - // private vars for handling selection and focus - // because group doesn't do it - int focused; - int* m_selected; - - rename_callback_type* rename_callback_; - paste_callback_type* paste_callback_; - Fl_Callback* context_callback_; - - int select_x1,select_y1,select_x2,select_y2; - - Fl_Button* find_button(edelib::String realpath) { - for (int i=0; iuser_data(); - if (realpath==tmp) return b; - } - return 0; - } -public: -#ifdef USE_FLU_WRAP_GROUP - FileIconView_(int X, int Y, int W, int H, char*label=0) : Flu_Wrap_Group(X,Y,W,H,label) -#else - FileIconView_(int X, int Y, int W, int H, char*label=0) : edelib::ExpandableGroup(X,Y,W,H,label) -#endif - { - end(); - box(FL_DOWN_BOX); - color(FL_BACKGROUND2_COLOR); -#ifdef USE_FLU_WRAP_GROUP - spacing(ICON_SPACING,ICON_SPACING); -#endif - - select_x1=select_y1=select_x2=select_y2=0; - focused=0; - m_selected = 0; - - rename_callback_ = 0; - paste_callback_ = 0; - context_callback_ = 0; - } - void insert(int row, FileItem *item) { - // update list of selected[] items - if (!m_selected) - m_selected = (int*)malloc(sizeof(int)*(children()+1)); - else - m_selected = (int*)realloc(m_selected,sizeof(int)*(children()+1)); - m_selected[children()]=0; - - Fl_Button* b = new Fl_Button(0,0,ICONW,ICONH); - b->box(FL_RFLAT_BOX); - b->color(FL_BACKGROUND2_COLOR); - b->align(FL_ALIGN_INSIDE|FL_ALIGN_CENTER|FL_ALIGN_CLIP); - - // Set the label - char buffer[FL_PATH_MAX]; - uint j=0; - for (uint i=0; iname.length(); i++,j++) { - buffer[j] = item->name[i]; - buffer[j+1] = '\0'; - if (buffer[j] == '@') { // escape @ - buffer[++j] = '@'; - buffer[j+1] = '\0'; - } - b->copy_label(buffer); - int lw =0, lh = 0; - fl_measure(b->label(), lw, lh); - if (lw>ICONW) { - if (j==i+2) { // already added 2 newlines - buffer[j-2]='.'; - buffer[j-1]='.'; - buffer[j]='.'; - buffer[j+1]='\0'; - j+=2; - break; // for - } else { // add newline - buffer[j+1]=buffer[j]; - buffer[j]='\n'; - buffer[j+2]='\0'; - j++; - } - } - } - while (j<=item->name.length()+2){ - buffer[j++]='\n'; - //buffer[j++]='L'; - } - buffer[j]='\0'; - b->copy_label(buffer); - - // Tooltip text - edelib::String tooltip = _("Name: ")+item->name; - if (item->size != "") tooltip += _("\nSize: ")+item->size; - tooltip += _("\nType: ")+item->description+_("\nDate: ")+item->date+_("\nPermissions: ")+item->permissions; - b->tooltip(strdup(tooltip.c_str())); - b->user_data(strdup(item->realpath.c_str())); - - // Set icon - edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_MEDIUM); - if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_MEDIUM,edelib::ICON_CONTEXT_MIMETYPE); - b->image(Fl_Shared_Image::get(icon.c_str())); - -#ifdef USE_FLU_WRAP_GROUP - Flu_Wrap_Group::insert(*b,row); -#else - edelib::ExpandableGroup::insert(*b,row); -#endif - //insert(*b,row); -- why doesn't this work? - redraw(); - } - void add(FileItem *item) { insert(children()+1, item); } - - void remove(FileItem *item) { - Fl_Button* b = find_button(item->realpath); - if (b) { -#ifdef USE_FLU_WRAP_GROUP - Flu_Wrap_Group::remove(*b); // note that FWG requires to dereference the pointer -#else - edelib::ExpandableGroup::remove(b); -#endif - //remove(b); - delete b; - } - } - - void remove(int row) { - if (row<1 || row>children()) return; - Fl_Button* b = (Fl_Button*)child(row-1); -#ifdef USE_FLU_WRAP_GROUP - Flu_Wrap_Group::remove(*b); // note that FWG requires to dereference the pointer -#else - edelib::ExpandableGroup::remove(b); -#endif - //remove(b); - delete b; - } - - - void update(FileItem *item) { - Fl_Button* b = find_button(item->realpath); - if (!b) return; - - // Tooltip text - edelib::String tooltip = _("Name: ")+item->name+_("\nSize: ")+item->size+_("\nType: ")+item->description+_("\nDate: ")+item->date+_("\nPermissions: ")+item->permissions; - b->tooltip(strdup(tooltip.c_str())); - - // Set icon - edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_MEDIUM); - if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_MEDIUM,edelib::ICON_CONTEXT_MIMETYPE); - b->image(Fl_Shared_Image::get(icon.c_str())); - - b->redraw(); - } - - // This is needed because update() uses path to find item in list - void update_path(const char* oldpath,const char* newpath) { - Fl_Button* b = find_button(oldpath); - if (!b) return; - b->user_data(strdup(newpath)); - } - - // Set item to "disabled" - void gray(int row) { - if (row<1 || row>children()) return; - Fl_Button* b = (Fl_Button*)child(row-1); - // FIXME this also means that item can't be selected - b->deactivate(); - } - // Set item to "enabled" - void ungray(int row) { - if (row<1 || row>children()) return; - Fl_Button* b = (Fl_Button*)child(row-1); - b->activate(); - } - - // Setup callback that will be used when renaming and dnd - void rename_callback(rename_callback_type* cb) { rename_callback_ = cb; } - void paste_callback(paste_callback_type* cb) { paste_callback_ = cb; } - void context_callback(Fl_Callback* cb) { context_callback_ = cb; } - - // Item real path (data value) - const char* path(int row) { - if (row<1 || row>children()) return 0; - Fl_Button* b = (Fl_Button*)child(row-1); - return (const char*)b->user_data(); - } - - // Is item selected? - int selected(int row) { - if (row<1 || row>children()) return 0; - int i=0; - while(m_selected[i]!=0) - if (m_selected[i++]==row) return 1; - return 0; - } - - // Select item (if value=1) or unselect (if value=0) - void select(int row, int value) { - if (row<1 || row>children()) return; - if (!m_selected) return; // shouldn't happen - set_focus(row); - int i=0; - Fl_Button* b = (Fl_Button*)child(row-1); - if (value) { - while (m_selected[i++]!=0); - m_selected[i-1]=row; - - b->color(FL_SELECTION_COLOR); - b->labelcolor(fl_contrast(FL_FOREGROUND_COLOR,FL_SELECTION_COLOR)); - b->redraw(); - } else { - while (m_selected[i]!=0) { - if (m_selected[i]==row) { - int j=i; - while (m_selected[j]!=0) { - m_selected[j]=m_selected[j+1]; - j++; - } - break; - } - i++; - } - - b->color(FL_BACKGROUND2_COLOR); - b->labelcolor(FL_FOREGROUND_COLOR); - b->redraw(); - } - } - - // Return nr. of widget that has keyboard focus - int get_focus() { -/*#ifdef USE_FLU_WRAP_GROUP -fprintf( stderr, "---- get_focus()=%d\n", focused); - return focused; -#else*/ - Fl_Widget* focus = Fl::focus(); - int i = find(focus); // Fl_Group method - if (ichildren()) return; - Fl_Button* b = (Fl_Button*)child(row-1); - -#ifdef USE_FLU_WRAP_GROUP - // scroll_to() will scroll widget to the top of view - // I prefer that view is scrolled just enough so icon is visible - // FIXME: sometimes b->y() is so large that it wraps around and becomes positive - if (b->y()+b->h() > y()+h()) { - int scrollto = scrollbar.value() + (b->y()+b->h()-y()-h())+1; -fprintf (stderr, "by: %d bh: %d y: %d h: %d sv: %d\n", b->y(), b->h(), y(), h(), scrollbar.value()); - ((Fl_Valuator*)&scrollbar)->value(scrollto); - redraw(); - draw(); // we need to relayout the group so that b->y() value is updated for next call - } - if (b->y() < y()) - scroll_to(b); -#else - // Not tested and probably broken: - Fl_Scrollbar* s = get_scroll(); - if (b->y() > s->value()+h()) { - // Widget is below current view - scrolly(b->y()+b->h()-h()); - } else if (b->y() < s->value()) { - // Widget is above current view - scrolly(b->y()); - } - // else { widget is visible, do nothing } -#endif - } - - // Set keyboard focus to given item - void set_focus(int row) { -fprintf( stderr, "---- set_focus(%d)\n", row); - if (row<1 || row>children()) return; - Fl_Button* b = (Fl_Button*)child(row-1); - b->take_focus(); - show_item(row); - focused=row; - } - - - // Overloaded handle() method, see inline comments for details - int handle(int e) { -//fprintf (stderr, " -- FileIconView - Event %d\n",e); - - // Fixes for focus management - // fltk provides focus management for members of Fl_Group, but it has minor problems: - // - when pressing Alt+Tab, focused widget is forgotten - // - if no widget is focused, arrow keys will navigate outside group - // - Tab has same effect as right arrow - - if (Fl::focus()->inside(this)) { // is focus inside? - int k = Fl::event_key(); -//fprintf(stderr, "event: %d key: %d\n",e,k); - if (k==FL_Up || k==FL_Down || k==FL_Left || k==FL_Right) { -#ifdef USE_FLU_WRAP_GROUP - // Wrap around - FWG only (due to methods such as above()) - // FL_KEYDOWN happens only if key is otherwise unhandled - if (e==FL_KEYDOWN) { - int x = get_focus()-1; - Fl_Widget* b = child(x); - Fl_Widget* b2; - if (k==FL_Up) b2=above(b); - if (k==FL_Down) b2=below(b); - // Bug in Flu_Wrap_Group - inconsistent behavior of below() - if (k==FL_Down && b2==b) b2=child(children()-1); - if (k==FL_Left) b2=left(b); - if (k==FL_Right) b2=next(b); - if (((k==FL_Up) || (k==FL_Left)) && x==0) - b2=child(children()-1); - if (((k==FL_Down) || (k==FL_Right)) && x==children()-1) - b2=child(0); - for (int i=0; ilabel()); - // Because widgets can be *very* nested, we go straight - // to window() - Fl_Group* master=window(); - Fl_Widget* jumpto=0; - int i=0; - while (jumpto==0 && master!=this) { - jumpto=master->child(i); -fprintf (stderr, " -- (%s)\n", jumpto->label()); - if (this->inside(jumpto) || !jumpto->visible()) - jumpto=0; - i++; - if (i==master->children()) { - int j; - for (j=0; jchildren(); j++) - if (master->child(j)->visible()) break; - if (j==master->children()) {// nothing is visible!? - master=(Fl_Group*)this; -fprintf (stderr, "WTF\n"); - }else{ - master=(Fl_Group*)master->child(j); -fprintf (stderr, " -> [%s]\n", master->label()); -} - i=0; - } - if (jumpto!=0 && !jumpto->take_focus()) - jumpto=0; // widget refused focus, keep searching - } -fprintf (stderr, "[X]\n"); - // if this is the only widget, do nothing - return 1; - } - } - if (e==FL_FOCUS) { - // Restore focused widget after losing focus - if (focused) set_focus(focused); - return 1; // We accept focus (this defaults to 0 with FL_GROUP) - } - - - // We accept mouse clicks - if (e==FL_PUSH && Fl::event_x()>x() && Fl::event_x() < x()+w()-Fl::scrollbar_size()) { - return 1; - } - - // Do callback on enter key - // (because icons don't have callbacks) - if (e==FL_KEYBOARD && Fl::event_key()==FL_Enter) { - do_callback(); - return 1; - } - - // Select using SPACE - if ((e==FL_KEYBOARD || e==FL_KEYUP) && Fl::event_key()==' ') { - if (selected(get_focus())) - select(get_focus(),0); - else - select(get_focus(),1); - - return 1; - } - - static bool laso=false; - static int dragx,dragy; - if (e==FL_DRAG) { -fprintf (stderr, "FL_DRAG! "); - if (!laso) { - // Drag inside child is dnd - int ex=Fl::event_x(); int ey=Fl::event_y(); - int inside=-1; - for (int i=0; ib->x()+SELECTION_EDGE && exx()+b->w()-SELECTION_EDGE && ey>b->y()+SELECTION_EDGE && eyy()+b->h()-SELECTION_EDGE) { - inside=i; break; - } - } - if (inside>=0) { - // If widget isn't selected, unselect everything else and select this - if (!selected(inside+1)) { - for (int i=0;icolor(FL_BACKGROUND2_COLOR); - b->labelcolor(FL_FOREGROUND_COLOR); - } - int i=0; - while (m_selected[i]!=0) m_selected[i++]=0; - select(inside+1,1); - redraw(); // show changes in selection state - } - - // Construct dnd string and start dnd - edelib::String selected_items; - for (int i=1; i<=children(); i++) - if (selected(i)==1) { - selected_items += "file://"; - selected_items += path(i); - selected_items += "\r\n"; - } - - // If paste_callback_ isn't set, that means we don't support dnd - if (paste_callback_) { - Fl::copy(selected_items.c_str(),selected_items.length(),0); - Fl::dnd(); - dragx=ex; dragy=ey; // to test if its close - } - return 1; - } else { - - // Drag to select (a.k.a. "laso") operation (outside widgets) - // Draw a dashed line around icons - fprintf (stderr, "- begin.\n"); - laso=true; - // Set coordinates for selection box (drawn in draw()) - select_x1=select_x2=Fl::event_x(); - select_y1=select_y2=Fl::event_y(); - } - } else { // if (!laso) -fprintf (stderr, "- box (%d,%d,%d,%d).\n",select_x1,select_y1,select_x2,select_y2); - select_x2=Fl::event_x(); - select_y2=Fl::event_y(); - redraw(); - } - return 1; - } - - // Mouse button released - if (e==FL_RELEASE) { -fprintf (stderr, "FL_RELEASE! "); - - // Unselect everything unless Shift or Ctrl is held - if (!Fl::event_state(FL_SHIFT) && !Fl::event_state(FL_CTRL)) { - for (int i=0;icolor(FL_BACKGROUND2_COLOR); - b->labelcolor(FL_FOREGROUND_COLOR); - } - redraw(); - int i=0; - while (m_selected[i]!=0) m_selected[i++]=0; - } - - // Stop laso operation - if (laso) { -fprintf (stderr, "- stop drag.\n"); - laso=false; - - // Order coordinates - int tmp; - if (select_x1>select_x2) { tmp=select_x1; select_x1=select_x2; select_x2=tmp; } - if (select_y1>select_y2) { tmp=select_y1; select_y1=select_y2; select_y2=tmp; } - - // Don't include the edges - select_x1 += SELECTION_EDGE; - select_y1 += SELECTION_EDGE; - select_x2 -= SELECTION_EDGE; - select_y2 -= SELECTION_EDGE; -fprintf(stderr, "After fixing the box coords: (%d,%d,%d,%d)\n", select_x1, select_y1, select_x2, select_y2); - - // Calculate which buttons were lasoed - int i; - for (i=0; ix()+w->w(); - int wy2 = w->y()+w->h(); - if (select_x2>w->x() && select_x1w->y() && select_y1label(), w->x(), w->y(), wx2, wy2); - } - } - // Shift key has no meaning with laso - - select_x1=select_x2=select_y1=select_y2=0; - redraw(); - - // Single click - } else { - // Find child that was clicked - int i; - for (i=0; itake_focus(); // Remove focus from all buttons - focused=0; - } - } - - // Right button - call context menu - if (Fl::event_button() == 3) { - Fl::event_is_click(0); // prevent doubleclicking with right button - if (context_callback_) context_callback_(this, (void*)path(get_focus())); - } - - // Double-click operation - if (Fl::event_clicks()) { - do_callback(); - } - } - - // DnD support - - // If paste_callback_ isn't set, that means we don't support dnd - if (paste_callback_) { -if (e==FL_DND_ENTER) { fprintf(stderr, "FL_DND_ENTER\n"); } -if (e==FL_DND_DRAG) { fprintf(stderr, "FL_DND_DRAG\n"); } -if (e==FL_DND_RELEASE) { fprintf(stderr, "FL_DND_RELEASE\n"); } - // Let the window manager know that we accept dnd - if (e==FL_DND_ENTER||e==FL_DND_DRAG) return 1; - -/* // Scroll the view by dragging to border - if (e==FL_DND_LEAVE) { - if (Fl::event_y()y()+h()) - position(position()+1); - return 1; - }*/ - - static bool dndrelease=false; - if (e==FL_DND_RELEASE) { -fprintf(stderr, "FL_DND_RELEASE '%s'\n", Fl::event_text()); - if (Fl::event_y()y()+h()) return 1; - // ^^ without this, sometimes there is crash in Fl::dnd() - - // Sometimes drag is accidental - if (abs(Fl::event_x()-dragx)>MIN_DISTANCE_FOR_DND || abs(Fl::event_y()-dragy)>MIN_DISTANCE_FOR_DND) { - dndrelease=true; - Fl::paste(*this,0); - } - return 1; - } - - if (e==FL_PASTE) { -fprintf(stderr, "FL_PASTE\n"); - if (!Fl::event_text() || !Fl::event_length()) return 1; -fprintf(stderr, "1 '%s' (%d)\n",Fl::event_text(),Fl::event_length()); - - // Paste comes from menu/keyboard - if (!dndrelease) { - paste_callback_(0); - return 1; - } - dndrelease=false; - - // Where is the user dropping? - // It it's inside an item that is directory, try it - int ex=Fl::event_x(); int ey=Fl::event_y(); - for (int i=0; ib->x()+SELECTION_EDGE && exx()+b->w()-SELECTION_EDGE && ey>b->y()+SELECTION_EDGE && eyy()+b->h()-SELECTION_EDGE) { - paste_callback_(path(i+1)); - return 1; - } - } - - // Nothing found... assume current directory - paste_callback_(0); - return 1; - } - - } - -#ifdef USE_FLU_WRAP_GROUP - return Flu_Wrap_Group::handle(e); -#else - return edelib::ExpandableGroup::handle(e); -#endif - - } - - // Override draw() for selection box - void draw() { -#ifdef USE_FLU_WRAP_GROUP - Flu_Wrap_Group::draw(); -#else - edelib::ExpandableGroup::draw(); -#endif - if (select_x1>0 && select_y1>0) { - fl_color(33); - fl_line_style(FL_DASH); - fl_line(select_x1,select_y1,select_x1,select_y2); - fl_line(select_x1,select_y2,select_x2,select_y2); - fl_line(select_x2,select_y2,select_x2,select_y1); - fl_line(select_x2,select_y1,select_x1,select_y1); - fl_line_style(0); - } - } - - // Override clear so that we can empty selected & focused values - void clear() { - focused=0; - if (m_selected) free(m_selected); - m_selected = 0; -#ifdef USE_FLU_WRAP_GROUP - Flu_Wrap_Group::clear(); - scroll_to_beginning(); // move scrollbar to top -#else - edelib::ExpandableGroup::clear(); -#endif - } -}; - - -class FileDetailsView_ : public EDE_Browser { -private: -// EDE_Browser* browser; - yada - // internal list of items -/* struct myfileitem { - struct FileItem *item; - myfileitem* previous; - myfileitem* next; - } *firstitem;*/ - - int findrow(edelib::String realpath) { - for (int i=1; i<=size(); i++) { - char *tmp = (char*)data(i); - if (realpath==tmp) return i; - } - return 0; - } - - - rename_callback_type* rename_callback_; - paste_callback_type* paste_callback_; - Fl_Callback* context_callback_; - - // Subclass Fl_Input so we can handle keyboard - class EditBox : public Fl_Input { - friend class FileDetailsView; - public: - EditBox(int x, int y, int w, int h, const char* label = 0) : Fl_Input(x,y,w,h,label) {} - int handle (int e) { - FileDetailsView_* view = (FileDetailsView_*)parent(); - if (e==FL_KEYBOARD && visible()) { - int k = Fl::event_key(); - if (Fl::event_key()==FL_Enter && visible()) { - view->hide_editbox(); - view->do_rename(); - } else if (k==FL_Up || k==FL_Down || k==FL_Page_Up || k==FL_Page_Down) { - view->hide_editbox(); - return 0; // let the view scroll - } else if (k==FL_F+2 || k==FL_Escape) - view->hide_editbox(); - else - Fl_Input::handle(e); - - return 1; // don't send keys to view - } - if (e==FL_MOUSEWHEEL && visible()) view->hide_editbox(); - return Fl_Input::handle(e); - } - }* editbox_; - int editbox_row; - - // show editbox at specified row and make the row "invisible" (bgcolor same as fgcolor) - void show_editbox(int row) { - if (!rename_callback_) return; - if (row<1 || row>size()) return; // nothing selected - if (text(row)[0]=='.' && text(row)[1]=='.' && text(row)[2]==column_char()) return; // can't rename "go up" button - - // unselect the row with focus - it's prettier that way - select(row,0); - - // Copy filename to editbox - char* filename = strdup(text(row)); - char* tmp = strchr(filename, column_char()); - if (tmp) *tmp='\0'; - editbox_->value(filename); - bucket.add(filename); - - // make the row "invisible" - editbox_row=row; - char* ntext = (char*)malloc(sizeof(char)*(strlen(text(row))+8)); // add 7 places for format chars - strncpy(ntext+7, text(row), strlen(text(row))); - strncpy(ntext, "@C255@.", 7); - ntext[strlen(text(row))+7]='\0'; - text(row,ntext); - bucket.add(ntext); - - - // calculate location for editbox - // "Magic constants" here are just nice spacing - int X=x()+get_icon(row)->w()+8; - int W=150; // reasonable - int H=get_icon(row)->h()+2; - int Y=y()+2 - position(); - void* item = item_first(); - for (int i=1; iresize(X,Y,W,H); - editbox_->show(); - editbox_->take_focus(); - editbox_->redraw(); - } - - void hide_editbox() { - fprintf(stderr, "hide_editbox()\n"); - editbox_->hide(); - - // Make the edited row visible again - char* ntext = (char*)malloc(sizeof(char)*(strlen(text(editbox_row))-6)); // remove 7 places for format chars - strncpy(ntext, text(editbox_row)+7, strlen(text(editbox_row))-7); - ntext[strlen(text(editbox_row))-7]='\0'; - text(editbox_row,ntext); - bucket.add(ntext); - } - - // Do the rename - void do_rename() { - fprintf(stderr, "editbox_cb()\n"); - rename_callback_(editbox_->value()); - //hide_editbox(); - } - - // Bucket class is used to prevent memleaks - // It stores pointers to allocated memory that will be cleaned up later - // Just remember to call empty() when needed - everything else is automatic :) - class Bucket { - void** items; - int size, capacity; - public: - Bucket() : size(0), capacity(1000) { - items = (void**)malloc(sizeof(void*)*capacity); - for (int i=0; i=capacity) { - capacity+=1000; - items = (void**)realloc(items,sizeof(void*)*capacity); - for (int i=capacity-1000; iend(); -// end(); -// resizable(browser->the_scroll()); -// resizable(browser->the_group()); -// resizable(browser); - const int cw[]={250,200,70,130,100,0}; - column_widths(cw); - column_char('\t'); - column_header("Name\tType\tSize\tDate\tPermissions"); - const SortType st[]={ALPHA_CASE_SORT, ALPHA_CASE_SORT, FILE_SIZE_SORT, DATE_SORT, ALPHA_SORT, NO_SORT}; - column_sort_types(st); - when(FL_WHEN_ENTER_KEY_ALWAYS); - //when(FL_WHEN_ENTER_KEY_CHANGED); - //first=0; - - editbox_ = new EditBox(0, 0, 0, 0); - editbox_->box(FL_BORDER_BOX); - editbox_->parent(this); - editbox_->hide(); - - rename_callback_ = 0; - paste_callback_ = 0; - context_callback_ = 0; - } -// ~FileDetailsView() { delete browser; } - - void insert(int row, FileItem *item) { - // Construct browser line - edelib::String value; - value = item->name+"\t"+item->description+"\t"+item->size+"\t"+item->date+"\t"+item->permissions; - char* realpath = strdup(item->realpath.c_str()); - EDE_Browser::insert(row, value.c_str(), realpath); // put realpath into data - bucket.add(realpath); -fprintf (stderr, "value: %s\n", value.c_str()); - - // Get icon - edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_TINY); - if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_TINY,edelib::ICON_CONTEXT_MIMETYPE); - set_icon(row, Fl_Shared_Image::get(icon.c_str())); - } - - - void add(FileItem *item) { insert(size()+1, item); } - - void remove(FileItem *item) { - int row = findrow(item->realpath); - if (row) EDE_Browser::remove(row); - } - void remove(int row) { EDE_Browser::remove(row); } // why??? - void update(FileItem *item) { - int row=findrow(item->realpath); - if (row==0) return; - - //EDE_Browser::remove(row); - //insert(row, item); - // this was reimplemented because a) it's unoptimized, b) adds everything at the end, - // c) causes browser to lose focus, making it impossible to click on something while - // directory is loading - - edelib::String value; - value = item->name+"\t"+item->description+"\t"+item->size+"\t"+item->date+"\t"+item->permissions; - char* realpath = strdup(item->realpath.c_str()); - text(row, value.c_str()); - data(row, realpath); - bucket.add(realpath); -fprintf (stderr, "value: %s\n", value.c_str()); - - // Get icon - edelib::String icon = edelib::IconTheme::get(item->icon.c_str(),edelib::ICON_SIZE_TINY); - if (icon=="") icon = edelib::IconTheme::get("misc",edelib::ICON_SIZE_TINY,edelib::ICON_CONTEXT_MIMETYPE); - set_icon(row, Fl_Shared_Image::get(icon.c_str())); - } - - // This is needed because update() uses path to find item in list - void update_path(const char* oldpath,const char* newpath) { - int row=findrow(oldpath); - if (row==0) return; - char* c = strdup(newpath); - data(row,c); - bucket.add(c); - } - - // Change color of row to gray - void gray(int row) { - if (text(row)[0] == '@' && text(row)[1] == 'C') return; // already greyed - - char *ntext = (char*)malloc(sizeof(char)*(strlen(text(row))+7)); // add 6 places for format chars - strncpy(ntext+6, text(row), strlen(text(row))); - strncpy(ntext, "@C25@.", 6); - ntext[strlen(text(row))+6]='\0'; - text(row,ntext); - bucket.add(ntext); - - // grey icon - but how to ungray? - Fl_Image* im = get_icon(row)->copy(); - im->inactive(); - set_icon(row,im); - - } - - void ungray(int row) { - if (text(row)[0] != '@' || text(row)[1] != 'C') return; // not greyed - - char *ntext = (char*)malloc(sizeof(char)*(strlen(text(row))-5)); // remove 6 places for format chars - strncpy(ntext, text(row)+6, strlen(text(row))-6); - ntext[strlen(text(row))-6]='\0'; - text(row,ntext); - bucket.add(ntext); - - // doesn't work - Fl_Image* im = get_icon(row)->copy(); - //im->refresh(); - //im->uncache(); // doesn't work - //im->color_average(FL_BACKGROUND_COLOR, 1.0); - //im->data(im->data(),im->count()); - set_icon(row,im); - - redraw(); // OPTIMIZE - } - - - // Overloaded handle for file renaming and dnd support - - int handle(int e) { - // Right click - -//fprintf(stderr, "Event: %d\n", e); - - if (e==FL_RELEASE && Fl::event_button()==3) { - void* item = item_first(); - int itemy=y()-position(); - int i; - for (i=1; i<=size(); i++) { - itemy+=item_height(item); - if (itemy>Fl::event_y()) break; - } - - set_focus(i); - Fl::event_is_click(0); // prevent doubleclicking with right button - if (context_callback_) context_callback_(this, data(i)); - return 1; - } -// if (e==FL_RELEASE && Fl::event_button()==3) { return 1; } - - /* ------------------------------ - Rename support - --------------------------------- */ -// fprintf (stderr, "Event: %d\n", e); - -/* if (e==FL_KEYBOARD) { - if (Fl::event_key()==FL_F+2) { - if (editbox_->visible()) - hide_editbox(); - else - show_editbox(get_focus()); - } - }*/ - if (e==FL_PUSH && editbox_->visible() && !Fl::event_inside(editbox_)) - hide_editbox(); // hide editbox when user clicks outside of it - if (e==FL_MOUSEWHEEL && editbox_->visible()) - hide_editbox(); // hide editbox when scrolling mouse - - // Click once on item that is already selected AND focused to rename it - static bool renaming=false; - if (e==FL_PUSH) renaming=false; - if (e==FL_PUSH && !editbox_->visible() && Fl::event_clicks()==0 && Fl::event_button()==1) { - const int* l = column_widths(); - if (Fl::event_x()x()+l[0]) - return Fl_Icon_Browser::handle(e); // we're only interested in first column - - // Is clicked item also focused? - void* item = item_first(); - int focusy=y()-position(); - for (int i=1; iFl::event_y()) break; - item=item_next(item); - } - if (Fl::event_y()focusy+item_height(item)) - return Fl_Icon_Browser::handle(e); // It isn't - if (selected(get_focus())!=1) - return Fl_Icon_Browser::handle(e); // If it isn't selected, then this action is select - - renaming=true; // On next event, we will check if it's doubleclick - } - - if (renaming && (e==FL_DRAG || e==FL_RELEASE || e==FL_MOVE) && Fl::event_is_click()==0) { - // Fl::event_is_click() will be >0 on doubleclick - show_editbox(get_focus()); - renaming=false; - return 1; // don't pass mouse event, otherwise item will become reselected which is a bit ugly - } - - /* ------------------------------ - Drag&drop support - --------------------------------- */ - - // If paste_callback_ isn't set, that means we don't support dnd - if (paste_callback_==0) return EDE_Browser::handle(e); - - // Let the window manager know that we accept dnd - if (e==FL_DND_ENTER||e==FL_DND_DRAG) return 1; - - // Scroll the view by dragging to border (only works on heading... :( ) - if (e==FL_DND_LEAVE) { - if (Fl::event_y()y()+h()) - position(position()+1); - return 1; - } - - // Don't unselect items on FL_PUSH cause that could be dragging - if (e==FL_PUSH && Fl::event_clicks()!=1) return 1; - - static int dragx,dragy; - if (e==FL_DRAG) { - edelib::String selected_items; - for (int i=1; i<=size(); i++) - if (selected(i)==1) { -// if (selected_items != "") selected_items += "\r\n"; - selected_items += "file://"; - selected_items += (char*)data(i); - selected_items += "\r\n"; - } - Fl::copy(selected_items.c_str(),selected_items.length(),0); - Fl::dnd(); - dragx = Fl::event_x(); dragy = Fl::event_y(); - return 1; // don't do the multiple selection thing from Fl_Browser - } - - static bool dndrelease=false; - if (e==FL_DND_RELEASE) { - if (Fl::event_y()y()+h()) return 1; - // ^^ without this, sometimes there is crash in Fl::dnd() - -fprintf(stderr, "FL_DND_RELEASE '%s'\n", Fl::event_text()); - // Sometimes drag is accidental - if (abs(Fl::event_x()-dragx)>MIN_DISTANCE_FOR_DND || abs(Fl::event_y()-dragy)>MIN_DISTANCE_FOR_DND) { - dndrelease=true; - Fl::paste(*this,0); - } - return 1; - } - if (e==FL_PASTE) { -fprintf(stderr, "FL_PASTE\n"); - if (!Fl::event_text() || !Fl::event_length()) return 1; -fprintf(stderr, "1 '%s' (%d)\n",Fl::event_text(),Fl::event_length()); - - // Paste comes from menu/keyboard - if (!dndrelease) { - paste_callback_(0); - return 1; - } - dndrelease=false; - - // Where is the user dropping? - // If it's not the first column, we assume the current directory - const int* l = column_widths(); - if (Fl::event_x()x()+l[0]) { - paste_callback_(0); - return 1; - } - - // Find item where stuff is dropped - void* item = item_first(); - int itemy=y()-position(); - int i; - for (i=1; i<=size(); i++) { - itemy+=item_height(item); - if (itemy>Fl::event_y()) break; - } - - paste_callback_((const char*)data(i)); - return 1; // Fl_Browser doesn't know about paste so don't bother it - } - - - return EDE_Browser::handle(e); - } - - // Setup callback that will be used when renaming and dnd - void rename_callback(rename_callback_type* cb) { rename_callback_ = cb; } - void paste_callback(paste_callback_type* cb) { paste_callback_ = cb; } - void context_callback(Fl_Callback* cb) { context_callback_ = cb; } - - // Avoid memory leak - void clear() { -fprintf(stderr, "Call FileView::clear()\n"); - bucket.empty(); - EDE_Browser::clear(); - } - - int visible() { return Fl_Widget::visible(); } -}; - - - - -/*class FileView : public Fl_Group { -public: - FileView(int X, int Y, int W, int H, char*label=0) : Fl_Group(X,Y,W,H,label) {} - - void insert(int row, FileItem *item); - void add(FileItem *item); - void remove(FileItem *item); - void update(FileItem *item); - - void update_path(const char* oldpath,const char* newpath); - - void gray(int row); - void ungray(int row); - - void rename_callback(rename_callback_type* cb); - void paste_callback(paste_callback_type* cb); -}; - - -class FileDetailsView : public FileView { -private: - FileDetailsView_ *browser; -public: - FileDetailsView(int X, int Y, int W, int H, char*label=0) : FileView(X,Y,W,H,label) { - browser = new FileDetailsView_(X,Y,W,H,label); - browser->end(); - end(); - } -// ~FileDetailsView() { delete browser; } - - void insert(int row, FileItem *item) { browser->insert(row,item); } - void add(FileItem *item) { browser->add(item); } - void remove(FileItem *item) { browser->remove(item); } - void update(FileItem *item) { browser->update(item); } - - void update_path(const char* oldpath,const char* newpath) { browser->update_path(oldpath,newpath); } - - void gray(int row) { browser->gray(row); } - void ungray(int row) { browser->ungray(row); } - - void rename_callback(rename_callback_type* cb) { browser->rename_callback(cb); } - void paste_callback(paste_callback_type* cb) { browser->paste_callback(cb); } - void context_callback(Fl_Callback* cb) { browser->context_callback(cb); } - - // Browser methods - const char* path(int i) { return (const char*)browser->data(i); } - int size() { return browser->size(); } - int selected(int i) { return browser->selected(i); } - void select(int i, int k) { browser->select(i,k); browser->middleline(i); } - int get_focus() { return browser->get_focus(); } - void set_focus(int i) { browser->set_focus(i); } - void remove(int i) { browser->remove(i); } - void clear() { browser->clear(); } - void callback(Fl_Callback*cb) { browser->callback(cb); } - - // These methods are used by do_rename - const char* text(int i) { return browser->text(i); } - void text(int i, const char* c) { return browser->text(i,c); } - uchar column_char() { return browser->column_char(); } -};*/ enum FileViewType { FILE_DETAILS_VIEW, @@ -1247,17 +74,19 @@ enum FileViewType { }; -class FileDetailsView : public Fl_Group { + + +class FileView : public Fl_Group { private: - FileDetailsView_* browser; - FileIconView_* icons; + FileDetailsView* browser; + FileIconView* icons; FileViewType m_type; public: - FileDetailsView(int X, int Y, int W, int H, char*label=0) : Fl_Group(X,Y,W,H,label) { - browser = new FileDetailsView_(X,Y,W,H,label); + FileView(int X, int Y, int W, int H, char*label=0) : Fl_Group(X,Y,W,H,label) { + browser = new FileDetailsView(X,Y,W,H,label); browser->end(); // browser->hide(); - icons = new FileIconView_(X,Y,W,H,label); + icons = new FileIconView(X,Y,W,H,label); icons->end(); end(); @@ -1287,7 +116,10 @@ public: } FileViewType type() { return m_type; } - // View methods + + // Methods that must be implemented by each view + + // Setter methods (forwarded to all views) void insert(int row, FileItem *item) { browser->insert(row,item); icons->insert(row,item); } void add(FileItem *item) { browser->add(item); icons->add(item); } void remove(FileItem *item) { browser->remove(item); icons->remove(item); } @@ -1295,32 +127,30 @@ public: void update_path(const char* oldpath,const char* newpath) { browser->update_path(oldpath,newpath); icons->update_path(oldpath,newpath); } - void gray(int row) { if (m_type==FILE_DETAILS_VIEW) browser->gray(row); else icons->gray(row); } - void ungray(int row) { if (m_type==FILE_DETAILS_VIEW) browser->ungray(row); else icons->ungray(row); } - + void callback(Fl_Callback*cb) { browser->callback(cb); icons->callback(cb);} void rename_callback(rename_callback_type* cb) { browser->rename_callback(cb); icons->rename_callback(cb); } void paste_callback(paste_callback_type* cb) { browser->paste_callback(cb); icons->paste_callback(cb); } void context_callback(Fl_Callback* cb) { browser->context_callback(cb); icons->context_callback(cb); } - // Browser methods - const char* path(int i) { if (m_type==FILE_DETAILS_VIEW) return (const char*)browser->data(i); else return icons->path(i); } - int size() { if (m_type==FILE_DETAILS_VIEW) return browser->size(); else return icons->children();} - int selected(int i) { if (m_type==FILE_DETAILS_VIEW) return browser->selected(i); else return icons->selected(i); } - void select(int i, int k) { if (m_type==FILE_DETAILS_VIEW) { browser->select(i,k); browser->middleline(i); } else { icons->select(i,k); icons->show_item(i); } } - int get_focus() { if (m_type==FILE_DETAILS_VIEW) return browser->get_focus(); else return icons->get_focus(); } - void set_focus(int i) { if (m_type==FILE_DETAILS_VIEW) browser->set_focus(i); else icons->set_focus(i);} void remove(int i) { browser->remove(i); icons->remove(i);} void clear() { browser->clear(); icons->clear();} - void callback(Fl_Callback*cb) { browser->callback(cb); icons->callback(cb);} + + // Methods forwarded to just the active view + void gray(int row) { if (m_type==FILE_DETAILS_VIEW) browser->gray(row); else icons->gray(row); } + void ungray(int row) { if (m_type==FILE_DETAILS_VIEW) browser->ungray(row); else icons->ungray(row); } + + const char* path(int i) { if (m_type==FILE_DETAILS_VIEW) return browser->path(i); else return icons->path(i); } + int size() { if (m_type==FILE_DETAILS_VIEW) return browser->size(); else return icons->children();} + int selected(int i) { if (m_type==FILE_DETAILS_VIEW) return browser->selected(i); else return icons->selected(i); } + void select(int i, int k) { if (m_type==FILE_DETAILS_VIEW) { browser->select(i,k); browser->show_item(i); } else { icons->select(i,k); icons->show_item(i); } } + int get_focus() { if (m_type==FILE_DETAILS_VIEW) return browser->get_focus(); else return icons->get_focus(); } + void set_focus(int i) { if (m_type==FILE_DETAILS_VIEW) browser->set_focus(i); else icons->set_focus(i);} int take_focus() { if (m_type==FILE_DETAILS_VIEW) return browser->take_focus(); else return icons->take_focus(); } - // These methods are used by do_rename() + // These methods are used by do_rename() - should become obsoleted const char* text(int i) { return browser->text(i); } void text(int i, const char* c) { return browser->text(i,c); } uchar column_char() { return browser->column_char(); } - - // Overloaded... - //int handle(int e) { if }; diff --git a/efiler/efiler.cpp b/efiler/efiler.cpp index f568f5f..7fde231 100644 --- a/efiler/efiler.cpp +++ b/efiler/efiler.cpp @@ -30,12 +30,14 @@ #include // for fl_dir_chooser, used in "Open location" #include #include // location bar +#include // for fl_register_images() #include #include #include #include #include +#include // for setting the icon theme #include "EDE_FileView.h" // our file view widget #include "EDE_DirTree.h" // directory tree @@ -48,7 +50,7 @@ Fl_Window* win; -FileDetailsView* view; +FileView* view; Fl_Menu_Bar* main_menu; Fl_Box* statusbar; DirTree* dirtree; @@ -797,7 +799,7 @@ fl_message_font(FL_HELVETICA, 12); dirtree->when(FL_WHEN_ENTER_KEY_ALWAYS|FL_WHEN_RELEASE_ALWAYS); dirtree->callback(tree_cb); - view = new FileDetailsView(150, menubar_height+location_bar_height, default_window_width-default_tree_width, default_window_height-menubar_height-location_bar_height-statusbar_height); + view = new FileView(150, menubar_height+location_bar_height, default_window_width-default_tree_width, default_window_height-menubar_height-location_bar_height-statusbar_height); view->callback(open_cb); // callbacks for file ops view->rename_callback(do_rename); diff --git a/efiler/fileops.h b/efiler/fileops.h index 5b6954a..6f227dd 100644 --- a/efiler/fileops.h +++ b/efiler/fileops.h @@ -32,7 +32,7 @@ void do_delete(); void do_rename(const char*); -extern FileDetailsView* view; +extern FileView* view; extern Fl_Window* win; extern Fl_Box* statusbar; extern char current_dir[];