/* * Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University * Copyright (c) 1991 Silicon Graphics, Inc. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that (i) the above copyright notices and this permission notice appear in * all copies of the software and related documentation, and (ii) the names of * Stanford and Silicon Graphics may not be used in any advertising or * publicity relating to the software without the specific, prior written * permission of Stanford and Silicon Graphics. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* * StringBrowser implementation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include StringBrowser::StringBrowser ( ButtonState* bs, int r, int c, boolean u, int h, const char* d ) { Init(bs, r, c, u, h, d); } StringBrowser::StringBrowser ( const char* name, ButtonState* bs, int r, int c, boolean u, int h, const char* d ) { SetInstance(name); Init(bs, r, c, u, h, d); } void StringBrowser::Init ( ButtonState* bs, int r, int c, boolean u, int h, const char* d ) { const int defaultSize = 256; SetClassName("StringBrowser"); input = new Sensor; input->Catch(DownEvent); input->Catch(KeyEvent); strbufsize = selbufsize = defaultSize; strbuf = new char*[strbufsize]; selbuf = new char*[selbufsize]; strcount = selcount = 0; display = nil; lineheight = 0; rows = r; columns = c; uniqueSel = u; singleClick = false; highlight = h; lastx = lasty = -1; subject = bs; Resource::ref(subject); done = d; perspective = new Perspective; InitTextDisplay(); } void StringBrowser::InitTextDisplay () { delete display; display = new TextDisplay; display->Draw(output, canvas); display->CaretStyle(NoCaret); for (int i = 0; i < strcount; ++i) { display->ReplaceText(i, strbuf[i], strlen(strbuf[i])); } if (canvas != nil) { output->ClearRect(canvas, 0, 0, xmax, ymax); } } StringBrowser::~StringBrowser () { Clear(); delete strbuf; delete selbuf; delete display; Resource::unref(subject); Resource::unref(perspective); } static void BufCheck (char**& buf, int& bufsize, int count, int index) { char** newbuf; if (index >= bufsize) { bufsize = (index+1) * 2; newbuf = new char*[bufsize]; Memory::copy(buf, newbuf, count*sizeof(char*)); delete buf; buf = newbuf; } } static void BufInsert ( const char* s, int index, char**& buf, int& bufsize, int& count ) { char** spot; index = (index < 0) ? count : index; if (index < count) { BufCheck(buf, bufsize, count, count+1); spot = &buf[index]; Memory::copy(spot, spot+1, (count - index)*sizeof(char*)); } else { BufCheck(buf, bufsize, count, index); spot = &buf[index]; } *spot = (char*)s; ++count; } static void BufRemove (int index, char** buf, int& count) { if (index < --count) { char** spot = &buf[index]; Memory::copy(spot+1, spot, (count - index)*sizeof(char*)); } } static int BufFind ( int index, char** srcbuf, int srccount, char** dstbuf, int dstcount ) { if (0 <= index && index < srccount) { const char* s = srcbuf[index]; if (s != nil) { for (int i = 0; i < dstcount; ++i) { if (dstbuf[i] == s) { return i; } } } } return -1; } void StringBrowser::Insert (const char* s, int index) { display->Draw(output, canvas); register Perspective* p = perspective; char* copy = new char[strlen(s)+1]; strcpy(copy, s); BufInsert(copy, index, strbuf, strbufsize, strcount); if (output != nil) { p->width = Math::max(p->width, output->GetFont()->Width(s)); } p->height += lineheight; p->cury += lineheight; p->Update(); if (index < strcount-1) { display->InsertLinesAfter(index-1, 1); } display->ReplaceText(index, s, strlen(s)); } void StringBrowser::Replace (const char* s, int index) { if (index < strcount) { display->Draw(output, canvas); register Perspective* p = perspective; char* old_string = String(index); delete old_string; char* copy = new char[strlen(s)+1]; strcpy(copy, s); strbuf[index] = copy; if (output != nil) { p->width = Math::max(p->width, output->GetFont()->Width(s)); } p->Update(); display->ReplaceText(index, s, strlen(s)); } } void StringBrowser::Remove (int index) { if (0 <= index && index < strcount) { display->Draw(output, canvas); register Perspective* p = perspective; char* string = String(index); if ( output != nil && p->width > columns * shape->hunits && p->width == output->GetFont()->Width(string) ) { UpdateWidth(); } Unselect(index); delete string; BufRemove(index, strbuf, strcount); p->height -= lineheight; p->cury -= lineheight; p->Update(); display->DeleteLinesAfter(index-1, 1); } } int StringBrowser::Index (const char* s) { for (int i = 0; i < strcount; ++i) { if (strcmp(s, strbuf[i]) == 0) { return i; } } return -1; } char* StringBrowser::String (int index) { return (0 <= index && index < strcount) ? strbuf[index] : nil; } void StringBrowser::Clear () { for (int i = 0; i < strcount; ++i) { delete strbuf[i]; } strcount = selcount = 0; InitTextDisplay(); Perspective np; *perspective = np; Resize(); } void StringBrowser::Select (int index) { if (index < strcount && !Selected(index)) { BufInsert(String(index), selcount, selbuf, selbufsize, selcount); display->Draw(output, canvas); display->Style(index, 0, index+1, -1, highlight); } } void StringBrowser::Unselect (int index) { int selindex; if (index < strcount && (selindex = SelectionIndex(index)) >= 0) { BufRemove(selindex, selbuf, selcount); display->Draw(output, canvas); display->Style(index, 0, index+1, -1, Plain); } } int StringBrowser::Selection (int selindex) { return BufFind(selindex, selbuf, selcount, strbuf, strcount); } int StringBrowser::SelectionIndex (int index) { return BufFind(index, strbuf, strcount, selbuf, selcount); } void StringBrowser::Browse () { Event e; e.target = nil; e.eventType = EnterEvent; Handle(e); } boolean StringBrowser::HandleDownEvent (Event& e) { boolean done = true; if (e.target == this) { if (e.button == LEFTMOUSE) { done = LeftButtonDown(e); } else if (e.button == MIDDLEMOUSE) { GrabScroll(e); } else if (e.button == RIGHTMOUSE) { RateScroll(e); } } else { UnRead(e); } return done; } boolean StringBrowser::HandleKeyEvent (Event& e) { boolean done = false; if (e.len != 0) { done = HandleChar(e.keystring[0]); } return done; } void StringBrowser::Handle (Event& e) { if (e.eventType == KeyEvent) { HandleKeyEvent(e); } else { boolean done = false; do { switch (e.eventType) { case DownEvent: done = HandleDownEvent(e); break; case KeyEvent: done = HandleKeyEvent(e); break; } if (!done) { Read(e); } } while (!done); } } boolean StringBrowser::HandleChar (char c) { int index = Selection(); int i; switch (c) { case SBFirstString: ScrollTo(0); break; case SBLastString: ScrollTo(strcount-1); break; case SBScrollDown: ScrollBy(1); break; case SBScrollUp: ScrollBy(-1); break; case SBSelectAll: if (!uniqueSel) { SelectAll(); } break; case SBUnselectAll: case SBUnselectAllAlt: UnselectAll(); break; case SBSelectPreviousString: UnselectAll(); --index; index = Math::max(0, Math::min(index, strcount-1)); Select(index); ScrollTo(index); break; case SBSelectNextString: UnselectAll(); ++index; index = Math::max(0, Math::min(index, strcount-1)); Select(index); ScrollTo(index); break; case SBSelectTopString: UnselectAll(); index = Math::max(0, Locate(0, ymax)); Select(index); break; case SBSelectBottomString: UnselectAll(); index = Math::min(Locate(0, 0), strcount-1); Select(index); break; case SBPageDown: ScrollBy((ymax+1) / lineheight); break; case SBPageUp: ScrollBy(-(ymax+1) / lineheight); break; case SBHalfPageDown: ScrollBy((ymax+1) / lineheight / 2); break; case SBHalfPageUp: ScrollBy(-(ymax+1) / lineheight / 2); break; default: for (i = 0; done[i] != '\0'; i++) { if (c == done[i]) { subject->SetValue(c); return true; } } break; } return false; } void StringBrowser::Adjust (Perspective& np) { register Perspective* p = perspective; float scale = (np.height == 0) ? 1 : float(p->height) / float(np.height); int x = p->x0 + int((np.curx - np.x0) * scale); int y = p->y0 + int((np.cury - np.y0) * scale); ScrollTo(x, y); } static Cursor* handCursor; static Cursor* upCursor; static Cursor* dnCursor; void StringBrowser::Reconfig () { if (handCursor == nil) { handCursor = new Cursor( new Bitmap( hand_bits, hand_width, hand_height, hand_x_hot, hand_y_hot ), new Bitmap(hand_mask_bits, hand_mask_width, hand_mask_height), output->GetFgColor(), output->GetBgColor() ); upCursor = new Cursor( new Bitmap( ufast_bits, ufast_width, ufast_height, ufast_x_hot, ufast_y_hot ), new Bitmap(ufast_mask_bits, ufast_mask_width, ufast_mask_height), output->GetFgColor(), output->GetBgColor() ); dnCursor = new Cursor( new Bitmap( dfast_bits, dfast_width, dfast_height, dfast_x_hot, dfast_y_hot ), new Bitmap(dfast_mask_bits, dfast_mask_width, dfast_mask_height), output->GetFgColor(), output->GetBgColor() ); } const Font* f = output->GetFont(); shape->hunits = f->Width("n"); shape->vunits = f->Height(); lineheight = shape->vunits; shape->Rect(shape->hunits*columns, shape->vunits*rows); shape->Rigid(hfil, hfil, shape->height - lineheight, vfil); singleClick = AttributeIsSet("singleClick"); const char* attrib = GetAttribute("clickDelay"); clickDelay = (attrib == nil) ? 250 : atoi(attrib); } void StringBrowser::UpdateWidth () { if (output != nil) { Perspective* p = perspective; const Font* f = output->GetFont(); p->width = columns * shape->hunits; for (int i = 0; i < Count(); ++i) { const char* s = String(i); p->width = Math::max(p->width, f->Width(s)); } } } void StringBrowser::InitPerspective (boolean scroll_to_top) { register Perspective* p = perspective; int old_top = p->height - p->cury - p->curheight; p->lx = p->curwidth = xmax+1; p->ly = p->curheight = ymax+1; p->sx = shape->hunits; p->sy = lineheight; p->height = Count() * lineheight; UpdateWidth(); if (scroll_to_top) { p->curx = 0; p->cury = p->height - p->curheight; } else { p->cury = p->height - p->curheight - old_top; } p->Update(); } void StringBrowser::Resize () { InitPerspective(perspective->curwidth == 0); if (lineheight != 0) { display->Draw(output, canvas); display->LineHeight(lineheight); display->Resize(0, -lineheight, xmax, ymax); } } void StringBrowser::Redraw (IntCoord l, IntCoord b, IntCoord r, IntCoord t) { display->Draw(output, canvas); display->Redraw(l, b, r, t); } void StringBrowser::Select (int dot, int mark) { for (int i = Math::min(dot, mark); i <= Math::max(dot, mark); ++i) { Select(i); } } void StringBrowser::SelectAll () { selcount = 0; for (int i = 0; i < strcount; ++i) { BufInsert(strbuf[i], i, selbuf, selbufsize, selcount); } display->Draw(output, canvas); display->Style(0, 0, strcount, -1, highlight); } void StringBrowser::Unselect (int dot, int mark) { for (int i = Math::min(dot, mark); i <= Math::max(dot, mark); ++i) { Unselect(i); } } void StringBrowser::UnselectAll () { selcount = 0; display->Draw(output, canvas); display->Style(0, 0, strcount, 0, Plain); } void StringBrowser::ScrollBy (int dx, int dy) { register Perspective* p = perspective; ScrollTo(p->curx + dx, p->cury + dy); } void StringBrowser::ScrollBy (int lines) { ScrollBy(0, -lines*lineheight); } void StringBrowser::ScrollTo (int x, int y) { register Perspective* p = perspective; int minx = 0; int maxx = Math::max(minx, p->width - p->curwidth/2); int maxy = p->height - p->curheight; int miny = Math::min(maxy, 1-lineheight); p->curx = Math::max(minx, Math::min(x, maxx)); p->cury = Math::max(miny, Math::min(y, maxy)); p->Update(); int topmargin = p->height - p->curheight - p->cury; int line = (lineheight == 0) ? 0 : topmargin / lineheight; display->Draw(output, canvas); display->Scroll(line, -p->curx, ymax); } void StringBrowser::ScrollTo (int index) { register Perspective* p = perspective; IntCoord y0 = p->y0 + p->cury; IntCoord y = p->height - (index+1)*lineheight - y0; if (y > ymax) { ScrollTo(0, y0 - (ymax-y)); } else if (y < 0) { y -= (p->curheight % lineheight == 0) ? 0 : lineheight; ScrollTo(0, y0 - (-y)); } } void StringBrowser::ScrollToView (IntCoord x, IntCoord y) { IntCoord dx = x < 0 ? x : x > xmax ? x - xmax : 0; IntCoord dy = y < 0 ? y : y > ymax ? y - ymax : 0; if (dx != 0 || dy != 0) { ScrollTo(perspective->curx + dx, perspective->cury + dy); } } int StringBrowser::Locate (IntCoord, IntCoord y) { register Perspective* p = perspective; y = Math::max(p->curheight % lineheight, Math::min(y, p->curheight-1)); return display->LineNumber(y); } void StringBrowser::Note (Event& e) { lasttime = e.timestamp; lastx = e.x; lasty = e.y; } boolean StringBrowser::DoubleClicked (Event& e) { if (e.eventType != DownEvent) { return false; } const int distThresh = 4; int time = abs(int(e.timestamp - lasttime)); int dist = abs(e.x - lastx) + abs(e.y - lasty); return time < clickDelay && dist < distThresh; } void StringBrowser::UpdateSelection (int d, int m, int style) { int oldl = Math::min(lastdot, lastmark); int oldr = Math::max(lastdot, lastmark); int newl = Math::min(d, m); int newr = Math::max(d, m); if (newr < oldl || newl > oldr) { // no overlap if (style == highlight) { Unselect(oldl, oldr); } if (style == highlight) { Select(newl, newr); } else { Unselect(newl, newr); } } else { // overlap if (newl < oldl) { if (style == highlight) { Select(newl, oldl); } else { Unselect(newl, oldl); } } else if (newl > oldl) { if (style == highlight) { Unselect(oldl, newl-1); } } if (newr > oldr) { if (style == highlight) { Select(oldr, newr); } else { Unselect(oldr, newr); } } else if (newr < oldr) { if (style == highlight) { Unselect(newr+1, oldr); } } } lastdot = d; lastmark = m; } boolean StringBrowser::LeftButtonDown (Event& e) { boolean status = false; if (DoubleClicked(e)) { subject->SetValue(done[0]); status = true; } else if (uniqueSel) { if (Selections() == 0) { Select(Locate(e.x, e.y)); } else { Unselect(Selection()); if (!e.shift) { Select(Locate(e.x, e.y)); } } } else { lastdot = lastmark = Locate(e.x, e.y); if (Selected(lastdot) && e.shift) { Unselect(lastdot); do { ScrollToView(e.x, e.y); UpdateSelection(lastdot, Locate(e.x, e.y), Plain); Poll(e); } while (e.leftmouse); } else { if (!e.shift) { UnselectAll(); } Select(lastdot); do { ScrollToView(e.x, e.y); UpdateSelection(lastdot, Locate(e.x, e.y), highlight); Poll(e); } while (e.leftmouse); } } Note(e); if (singleClick) { subject->SetValue(done[0]); status = true; } return status; } void StringBrowser::GrabScroll (Event& e) { int y = e.y; Cursor* origCursor = GetCursor(); SetCursor(handCursor); do { ScrollBy(0, y - e.y); y = e.y; Poll(e); } while (e.middlemouse); SetCursor(origCursor); } void StringBrowser::RateScroll (Event& e) { Cursor* origCursor = GetCursor(); int y = e.y; do { ScrollBy(0, e.y - y); if (e.y - y < 0) { SetCursor(dnCursor); } else { SetCursor(upCursor); } Poll(e); } while (e.rightmouse); SetCursor(origCursor); }